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;使用临时的;使用文件排序