1

データベースのクエリ/インデックス作成方法を理解するのに苦労しています。

状況は非常に単純です。ユーザーがカテゴリにアクセスするたびに、ユーザーのアクセス日が保存されます。私の目標は、ユーザーが最後にアクセスした後に要素が追加されたカテゴリを一覧表示することです。

2つの表は次のとおりです。

CREATE TABLE `elements` (
  `category_id` int(11) NOT NULL,
  `element_id` int(11) NOT NULL,
  `title` varchar(255) NOT NULL,
  `added_date` datetime NOT NULL,
  PRIMARY KEY (`category_id`,`element_id`),
  KEY `index_element_id` (`element_id`)
)

CREATE TABLE `categories_views` (
  `member_id` int(11) NOT NULL,
  `category_id` int(11) NOT NULL,
  `view_date` datetime NOT NULL,
  PRIMARY KEY (`member_id`,`category_id`),
  KEY `index_element_id` (`category_id`)
)

クエリ:

SELECT
    categories_views.*,
    elements.category_id
FROM
    elements
    INNER JOIN categories_views ON (categories_views.category_id = elements.category_id)
WHERE
    categories_views.member_id = 1
    AND elements.added_date > categories_views.view_date
GROUP BY elements.category_id

説明:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: elements
         type: ALL
possible_keys: PRIMARY
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 89057
        Extra: Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: categories_views
         type: eq_ref
possible_keys: PRIMARY,index_element_id
          key: PRIMARY
      key_len: 8
          ref: const,convert.elements.category_id
         rows: 1
        Extra: Using where

各テーブルに約10万行あるため、クエリには約0.3秒かかります。これは、Webコンテキストのすべてのユーザーアクションに対して実行する必要があるものには長すぎます。

可能であれば、どのインデックスを追加する必要がありますか、またはファイルソートと一時テーブルの使用を回避するためにこのクエリをどのように書き直す必要がありますか?

4

1 に答える 1

1

各メンバーのcategory_viewsの数が比較的少ない場合は、別のクエリをテストすることをお勧めします。

SELECT v.*
  FROM categories_views v
 WHERE v.member_id = 1
   AND EXISTS 
       ( SELECT 1
           FROM elements e
          WHERE e.category_id = v.category_id
            AND e.added_date > v.view_date
       )

そのクエリの最適なパフォーマンスを得るには、インデックスがあることを確認する必要があります。

... ON elements (category_id, added_date)

... ON categories_views (member_id, category_id) 

categories_views注:テーブルの主キーはである可能性があります(member_id, category_id)。これは、適切なインデックスがすでに存在することを意味します。

(元のクエリから理解できる限り)categories_viewsテーブルには、ユーザーのカテゴリの「最新の」ビューのみが含まれている、つまりmember_id, category_id一意であると想定しています。元のクエリが正しい結果セットを返している場合(ユーザーによるそのカテゴリの「最後のビュー」以降に追加された「新しい」要素を持つカテゴリのみを返す場合、そうでない場合は存在する)は、そうである必要があるように見えます。カテゴリ内の最新(max )要素よりも新しいものがあったとしても、テーブル内の「古い」view_date値のいずれかcategories_viewsがカテゴリの包含をトリガーします。view_dateadded_date

そうでない場合、つまり(member_id,category_id)一意でない場合は、クエリを変更する必要があります。


元の質問のクエリは少し不可解でelement_views、テーブル名またはテーブルエイリアスとして参照されますが、EXPLAIN出力には表示されません。element_viewsの同義語であることが意図されているという仮定の下で行っていcategories_viewsます。


elements元のクエリの場合、テーブルにカバーインデックスを追加します。

 ... ON elements (category_id, added_date)

そこにある目標は、「インデックスの使用」を示すためのExplain出力を取得することです。

インデックスを追加してみることもできます。

 ... ON categories_views (member_id, category_id, added_date)

(選択リストの)categories_viewテーブルからすべての列を取得するには、クエリはテーブル内のページにアクセスする必要があります(これらの列をすべて含むインデックスがない場合。目標は行数を減らすことです。インデックスからすべて(またはほとんど)の述語を満たして、行を見つけるためにデータページにアクセスする必要があります。


テーブルcategory_idから列を返す必要がありますか?内部結合述部のため、これがテーブルの列elementsと同じ値であることをすでに知っていますか?category_idcategories_views


于 2013-01-14T23:43:26.810 に答える