2013-01-14 81 views
1

我很难确定如何查询/索引数据库。MySQL:将where子句中的日期与连接进行比较

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

这里有两个表:

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.3s ,这对于Web上下文中的每个用户操作都应该执行的内容来说太长了。

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

+0

您提到表'element_views'。但它没有加入,也没有列在'FROM'中。你能把它从这个查询中排除吗? – mvp

回答

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是唯一的。如果原始查询返回了正确的结果集(如果它自从用户的该类别的“最后一个视图”以来唯一返回的具有“新”元素的类别;否则,存在在categories_views表中的任何“老” view_date值将触发类的包容性,即使有,这是晚于在一个类别最新(最大added_date)元素的新view_date

如果是这种情况并非如此,即(member_id,category_id)不是唯一的,那么查询将需要改变。


查询原questio n有点令人费解,它将element_views作为表名或表别名,但不会出现在EXPLAIN输出中。我打算假设element_views是为了categories_views的同义词。


对于原来的查询,在elements表添加一个覆盖索引:

... ON elements (category_id, added_date) 

的目标有得到解释输出显示“使用索引”

您也可能尝试添加索引:

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

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


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