MySQL:比较where子句与连接的日期

时间:2013-01-14 23:19:46

标签: mysql join indexing

我很难确定如何查询/索引数据库。

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

以下是两个表格:

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上下文中的每个用户操作执行的内容来说太长了。

如果可能,我应该添加哪些索引,或者我应该如何重写此查询以避免使用filesorts和临时表?

1 个答案:

答案 0 :(得分: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表仅包含用户类别的“最新”视图,即{{1} } 是独特的。看起来情况必须如此,如果原始查询返回正确的结果集(如果它的唯一返回类别自用户的“最后一个视图”以来添加了“新”元素;否则,存在member_id, category_id表中的任何“较旧”view_date值都会触发包含该类别,即使有一个比最新categories_views更新的view_date更新added_date })类别中的元素。

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


原始问题中的查询有点令人费解,它将element_views引用为表名或表别名,但这不会出现在EXPLAIN输出中。我假设element_viewscategories_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列中的值相同,因为内连接谓词?


相关问题