MySQL 子查询优化 - where not in(子查询)

Posted

技术标签:

【中文标题】MySQL 子查询优化 - where not in(子查询)【英文标题】:MySQL Subquery Optimization - where not in(subquery) 【发布时间】:2013-04-26 22:24:07 【问题描述】:

我正在尝试优化以下查询。我认为外部连接可以解决问题,但我不知道如何将它组合在一起。

// ---------------------------------
// 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

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

【问题讨论】:

【参考方案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
    第一个连接只获取具有给定属性的文章。 第二个连接采用第一个连接的结果并返回带有 user_id 的行以及第一个结果中没有 user_id 的所有剩余行。(基本上所有具有属性 132385 且 user_id 为 10 或 NULL 的文章) 那么我们想要的只是 user_id 为 NULL 的结果

尽量避免嵌套查询,让引擎完成它的工作。请注意,您可以在最后标记其他过滤器(DATE、ORDER BY)。

【讨论】:

【参考方案2】:

试试这个查询

 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)

希望这会有所帮助。

【讨论】:

【参考方案3】:

我建议不要将子查询用作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)

希望对你有帮助

【讨论】:

【参考方案4】:

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

【讨论】:

以上是关于MySQL 子查询优化 - where not in(子查询)的主要内容,如果未能解决你的问题,请参考以下文章

MySQL:在 WHERE 子句中带有 NOT IN 的从属子查询非常慢

Mysql在where子句中优化子查询

MySQL 查询优化 - 子查询 & 使用 where;使用临时的;使用文件排序

WHERE 子句中带有子查询的 MYSQL UPDATE 查询 - 优化

优化 MySQL NOT IN( 查询

如何使用 WHERE IN 子查询优化 SQL 查询