我正在使用MySQL数据库并尝试优化以下查询:
SELECT * FROM main
WHERE (name IS NOT NULL AND name != '')
AND user_id NOT IN
( SELECT user_id FROM process
WHERE sns_id = 1 AND (process.status=1 OR process.status=2)
AND process.updated_at > 1392335789207) LIMIT 100;
基本上我想确保从user_id
中选择行时,process
表中的所有main
都不包括在内。
我尝试使用EXISTS
进行优化,但这似乎没有产生更好的效果。
我该如何优化此查询?
答案 0 :(得分:1)
你可以使用 查询:
SELECT *
FROM main
WHERE name IS NOT NULL
AND name != ''
AND user_id NOT EXISTS
(SELECT user_id
FROM process
WHERE sns_id = 1
AND process.status in (1,2)
AND process.updated_at > 1392335789207
AND process.user_id = main.user_id)
LIMIT 100
您说没有重复的用户ID,因此您可以尝试使用此查询,这可能是最快的解决方案:
SELECT m.*
FROM main m
LEFT JOIN process p
ON p.user_id = m.user_id
AND p.sns_id = 1
AND p.status in (1,2)
AND p.updated_at > 1392335789207
WHERE m.name IS NOT NULL
AND m.name != ''
AND p.user_id is null
LIMIT 100
答案 1 :(得分:0)
我相信使用NOT IN (subquery)
,该子查询将针对外部查询返回的每一行进行评估。如果要处理500,000行,则执行相同子查询的500,000行。 process
的索引几乎是性能所必需的。
大型集合的(通常)更有效的方法是使用反连接模式。
为此,我们进行OUTER JOIN以从进程中查找匹配的行,然后我们丢弃找到匹配的任何行。我们剩下的是main中没有匹配行的行。相当于NOT IN(子查询),但(通常)对大型集合更有效。
SELECT m.*
FROM main m
LEFT
JOIN ( SELECT p.user_id
FROM process p
WHERE p.sns_id = 1
AND p.status IN (1,2)
AND p.updated_at > 1392335789207
GROUP BY p.user_id
) r
ON r.user_id = m.user_id
WHERE (m.name IS NOT NULL AND m.name != '')
AND r.user_id IS NULL
LIMIT 100;
请注意,我们在连接操作上使用LEFT
关键字来指定我们希望返回main
中的所有行,即使r
没有匹配的行也是如此。我们在WHERE子句中包含r.user_id IS NULL
作为谓词,以排除与r
匹配的行。 (JOIN谓词(r.user_id = m.user_id
)中的相等比较符保证来自r
的任何匹配行都将具有{NOT}的user_id
值。因此{{1}的任何NULL值}必须是未找到r.user_id
匹配的结果。
为了提高性能,您可能希望覆盖r
表中可用的索引,例如。
process
内联视图查询ON process ('user_id, `updated_at`, `status`, `sns_id`)
中的GROUP BY
并非绝对必要,但我们并不需要为同一r
返回多行,一个就足够了。对于前导列为user_id
的索引,MySQL通常会使用索引操作来执行GROUP BY,从而避免user_id
操作。我们也不需要为Using filesort
返回NULL值。