MySQL子查询优化 - 不在(子查询)中

时间:2013-04-26 22:24:08

标签: php mysql query-optimization

我正在尝试优化以下查询。我认为外部联接可以解决这个问题,但是我无法理解如何将它组合在一起。

// ---------------------------------
// Simplified representation of data
// ---------------------------------
create table views (
   user_id,
   article_id
)

create table article_attributes (
   article_id,
   article_attribute_id
)

create table articles (
   id,
   title,
   date
)

Views表有数千万条记录。 文章表有几十万。

我正在尝试将所有文​​章与某个与之关联的属性进行匹配,并且用户尚未查看过该文章。

我尝试了什么,但不能很好地扩展:

select a.title, a.sid as article_id, a.total_views as times_read, a.date 
from articles a 
join article_attributes att on att.article_id = a.sid 

where a.sid not in( 
   select v.article_id 
   from views v
   join article_attributes att on att.article_id = v.article_id 
   where user_id = 132385 
   and att.article_attribute_id = 10
   group by v.article_id 
) 
and att.article_attribute_id = 10 
and a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day) 
order by total_views desc 
limit 5

这种方法很好,但是用户查看的文章越多,速度就越慢。任何想法或建议将不胜感激。

4 个答案:

答案 0 :(得分:1)

SELECT a.title, a.sid AS article_id, a.total_views AS times_read, a.date
FROM articles a 
    JOIN article_attributes att 
        ON a.id = att.article_id AND att.article_attribute_id = 10 
    LEFT JOIN views v 
        ON a.id = v.article_id AND v.user_id = 132385  
WHERE v.user_id IS NULL
  1. 第一个联接只会获取具有给定属性的文章。
  2. 第二个连接获取第一个连接的结果,并返回带有user_id的行和第一个没有user_id的结果中的所有剩余行。(基本上所有文章的属性为132385,user_id为10或NULL)< / LI>
  3. 然后我们想要的是user_id为NULL的结果
  4. 尽量避免嵌套查询,让引擎完成它的工作。请注意,您可以在其他过滤器(DATE,ORDER BY)上标记。

答案 1 :(得分:1)

尝试此查询

 select a.title, a.sid as article_id, a.total_views as times_read, a.date 
 from 
    articles a 
 left join 
    views v
 on 
    a.sid = v.article_id AND v.article_id is null
 join 
    article_attributes att 
 on 
    att.article_id = v.article_id AND v.user_id = 132385 AND att.article_attribute_id = 10
 where  
     a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day) 
 order by 
    total_views desc limit 5

articles(total_views, sid, date)

创建必要的索引

view(article_id, user_id)

article_attributes(article_id, article_attribute_id)

希望这有帮助。

答案 2 :(得分:0)

我建议在连接中使用子查询,而不是将子查询用作where条件。另外,我建议您不要在子查询中使用group by,而是select distinct

select
    a.title, a.sid as article_id, a.total_views as times_read, a.date 
from
    (articles a
    inner join article_attributes att on a.sid = att.article_id)
    left join (
        select distinct
            v.article_id 
        from views v
            inner join article_attributes att on v.article_id = att.article_id
        where
            user_id = 132385
            and att.article_atribute_id = 10
        ) as b on a.sid = b.article_id
where
    b.article_id is null
    and att.article_attribute_id = 10 
    and a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day)

希望这有帮助

答案 3 :(得分:0)

EXISTS的效果应优于IN

SELECT a.title,
       a.sid AS article_id,
       a.total_views AS times_read,
       a.date
FROM articles a
JOIN article_attributes att ON att.article_id = a.sid
WHERE NOT EXISTS (SELECT 0
                  FROM views v
                  JOIN article_attributes att ON att.article_id = v.article_id
                  WHERE user_id = 132385
                  AND att.article_attribute_id = 10
                  AND v.article_id = a.sid )
 AND att.article_attribute_id = 10
 AND a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
 ORDER BY total_views DESC LIMIT 5