我有以下有点复杂的 sql
查询,它的性能很糟糕,“肯定”是由于 where
子句中的内部查询。在某些情况下,它需要一分钟多的时间。有人知道如何重写此查询以获得更好的性能吗?
查询:
SELECT DISTINCT t.id as taskId, t.name as taskName,
t.startdate as taskStartDate, t.enddate as taskEndDate,
t.proj_id as taskProjectId
FROM PROJECT p, EMPL_PROJ ep, TASK t, TIMERECORD tr
WHERE
ep.empl_id = ? AND
ep.proj_id = p.id AND
ep.proj_id = t.proj_id AND
((p.startdate IS NULL AND p.enddate IS NULL) OR
(p.startdate IS NULL AND p.enddate >= ?) OR
(p.enddate IS NULL AND p.startdate <= ? + INTERVAL 6 DAY) OR
(p.startdate <= ? + INTERVAL 6 DAY AND p.enddate >= ?) ) AND
((t.startdate IS NULL AND t.enddate IS NULL) OR
(t.startdate IS NULL AND t.enddate >= ?) OR
(t.enddate IS NULL AND t.startdate <= ? + INTERVAL 6 DAY) OR
(t.startdate <= ? + INTERVAL 6 DAY AND t.enddate >= ?)) AND
(
(ep.empl_id = tr.empl_id AND
ep.proj_id = tr.proj_id AND
t.id = tr.task_id AND tr.day <= ? + INTERVAL 7 DAY AND
tr.day >= ? + INTERVAL -14 DAY
) OR
(
(SELECT count(*)
FROM TIMERECORD tr2
WHERE
tr2.empl_id=ep.empl_id AND
tr2.proj_id=p.id AND tr2.day <= ? + INTERVAL 7 DAY AND
tr2.day >= ? + INTERVAL -14 DAY) <= 0
)
)
我正在使用 mysql 服务器 5.1.40。
编辑 (2): 有了评论和答案,我来到了这个查询,它在一秒钟内执行(差不多一分钟就不错了!)
SELECT DISTINCT t.id as taskId, t.name as taskName,
t.startdate as taskStartDate, t.enddate as taskEndDate,
t.proj_id as taskProjectId
FROM (PROJECT p INNER JOIN EMPL_PROJ ep ON ep.proj_id = p.id)
INNER JOIN TASK t ON p.id=t.proj_id
INNER JOIN TIMERECORD tr ON tr.empl_id=ep.empl_id AND tr.proj_id=ep.proj_id
AND tr.task_id=t.id
WHERE
ep.empl_id = ? AND
((p.startdate IS NULL AND p.enddate IS NULL) OR
(p.startdate IS NULL AND p.enddate >= ?) OR
(p.enddate IS NULL AND p.startdate <= ? + INTERVAL 6 DAY) OR
(p.startdate <= ? + INTERVAL 6 DAY AND p.enddate >= ?) ) AND
((t.startdate IS NULL AND t.enddate IS NULL) OR
(t.startdate IS NULL AND t.enddate >= ?) OR
(t.enddate IS NULL AND t.startdate <= ? + INTERVAL 6 DAY) OR
(t.startdate <= ? + INTERVAL 6 DAY AND t.enddate >= ?)) AND
(
(
tr.day <= ? + INTERVAL 7 DAY AND
tr.day >= ? + INTERVAL -14 DAY
) OR
(
NOT EXISTS(SELECT *
FROM TIMERECORD tr2 INNER JOIN EMPL_PROJ ON tr2.empl_id=EMPL_PROJ.empl_id
INNER JOIN PROJECT ON PROJECT.id=tr2.proj_id
WHERE
tr2.day BETWEEN ? + INTERVAL -14 DAY AND ? + INTERVAL 7 DAY)
)
)
ORDER BY p.id, t.id
最大的贡献是建议使用 NOT EXISTS
方法(我标记为正确)的答案以及不要混合使用 explicit
和 implicit JOIN
的。
感谢大家!
最佳答案
当您似乎只需要一个 NOT EXISTS 时,您正在使用 COUNT(*)...
(
(SELECT count(*)
FROM TIMERECORD tr2
WHERE
tr2.empl_id=ep.empl_id AND
tr2.proj_id=p.id AND tr2.day <= ? + INTERVAL 7 DAY AND
tr2.day >= ? + INTERVAL -14 DAY) <= 0
)
替换为
(
NOT EXISTS(SELECT *
FROM TIMERECORD tr2
WHERE
tr2.empl_id=ep.empl_id AND
tr2.proj_id=p.id AND tr2.day <= ? + INTERVAL 7 DAY AND
tr2.day >= ? + INTERVAL -14 DAY)
)
现在,如果 TIMERECORD 确实存在,那部分 where 子句将短路为 FALSE(NOT TRUE),而不必计算每个 TIMERECORD。
关于mysql - where 子句中查询的查询性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9872045/