1

我很难弄清楚如何查询/索引数据库。

情况很简单。每次用户访问一个类别时,都会存储他/她的访问日期。我的目标是列出用户最近一次访问后添加元素的类别。

下面是两张表:

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

每个表中有大约 100k 行,查询大约需要 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是唯一的。如果原始查询返回正确的结果集(如果它仅返回自用户对该类别的“最后一次查看”以来添加了“新”元素的类别,则看起来必须如此;否则,存在表中的任何“较旧”view_datecategories_views都会触发该类别的包含,即使有一个比类别中的最新 (max) 元素view_date晚的更新。added_date

如果不是这种情况,即(member_id,category_id)不是唯一的,则需要更改查询。


原始问题中的查询有点令人费解,它引用element_views为表名或表别名,但这并没有出现在 EXPLAIN 输出中。我假设它element_views是 . 的同义词categories_views


对于原始查询,在elements表上添加覆盖索引:

 ... ON elements (category_id, added_date)

目标是让解释输出显示“使用索引”

您也可以尝试添加索引:

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

要从 categories_view 表中获取所有列(对于选择列表),查询将不得不访问表中的页面(除非存在包含所有这些列的索引。目标是减少行数需要在数据页上访问以通过索引满足所有(或大部分)谓词来查找行。


是否有必要category_id从表中返回列elements?由于内部连接谓词,我们不是已经知道这与表中的category_id列中的值相同吗?categories_views


于 2013-01-14T23:43:26.810 回答