2013-07-11 133 views
1

我受限于传统数据库结构并需要一些统计结果。下面的查询工作,但效率低,速度慢...如何使用重复的子查询优化SQL Server查询

SELECT various, other, native, columns, 
    (SELECT client FROM clients WHERE id = clientid) AS client, 
    (SELECT name FROM categories WHERE id = (SELECT categoryid FROM clients WHERE id = clientid)) AS category, 
    (SELECT fullname FROM staff WHERE id = producerid) AS producer, 
    ISNULL((SELECT SUM(amount) FROM JobsVoiceWork v WHERE v.jobid = j.id),0) AS voicecosts, 
    (SELECT COUNT(*) FROM Scripts s WHERE s.jobid = j.id) AS numberofscriptscompleted, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id),0)/60 AS totaltime, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 3 AND jobpart = 'Add'),0)/60 AS PartAdd, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 3 AND jobpart = 'Update'),0)/60 AS PartUpdate, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 3 AND jobpart = 'Produce'),0)/60 AS PartProduce, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 3 AND jobpart = 'Amend'),0)/60 AS PartAmend, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 4),0)/60 AS EditProducerError, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 8),0)/60 AS EditVoiceError, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 1),0)/60 AS EditClientError, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 2),0)/60 AS EditEntryError, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 5),0)/60 AS EditPronunciation, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 6),0)/60 AS EditRemixRequest, 
    ISNULL((SELECT SUM(duration) FROM TimeLog WHERE jobid = j.id AND jobeditid = 7),0)/60 AS EditRevoiceRequest 
FROM Jobs j 

我有显示查询的简化版本,但我已经包含重复子查询清楚地表明了低效率。我尝试了各种表连接方案,但我无法提高性能。

看起来喜欢它应该有可能改善。有没有办法?

+0

你可以尝试'(SELECT * FROM Timelog其中作业ID = j.id)作为jobtl'作为第一,那么你以后的子查询的选择jobtl而不是TimeLog。这应该会给你一个更小的数据集来处理你的所有子查询。 – 2013-07-11 03:37:47

+0

你使用什么数据库? –

回答

4

您可以使用CASE声明以消除冗余的子查询,像:

SELECT various, other, native, columns, 
    (SELECT client FROM clients WHERE id = clientid) AS client, 
    (SELECT name FROM categories WHERE id = (SELECT categoryid FROM clients WHERE id = clientid)) AS category, 
    (SELECT fullname FROM staff WHERE id = producerid) AS producer, 
    ISNULL((SELECT SUM(amount) FROM JobsVoiceWork v WHERE v.jobid = j.id),0) AS voicecosts, 
    (SELECT COUNT(*) FROM Scripts s WHERE s.jobid = j.id) AS numberofscriptscompleted, 
    ISNULL(SUM(t.duration),0)/60 AS totaltime, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 3 AND t.jobpart = 'Add' THEN t.duration ELSE 0 END),0)/60 AS PartAdd, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 3 AND t.jobpart = 'Update' THEN t.duration ELSE 0 END),0)/60 AS PartUpdate, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 3 AND t.jobpart = 'Produce' THEN t.duration ELSE 0 END),0)/60 AS PartProduce, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 3 AND t.jobpart = 'Amend' THEN t.duration ELSE 0 END),0)/60 AS PartAmend, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 4 THEN t.duration ELSE 0 END),0)/60 AS EditProducerError, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 8 THEN t.duration ELSE 0 END),0)/60 AS EditVoiceError, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 1 THEN t.duration ELSE 0 END),0)/60 AS EditClientError, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 2 THEN t.duration ELSE 0 END),0)/60 AS EditEntryError, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 5 THEN t.duration ELSE 0 END),0)/60 AS EditPronunciation, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 6 THEN t.duration ELSE 0 END),0)/60 AS EditRemixRequest, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 7 THEN t.duration ELSE 0 END),0)/60 AS EditRevoiceRequest 
    FROM Jobs j 
    LEFT JOIN Timelog t 
     ON j.id = t.jobid 
+0

提高370%!谢谢! – user2570935

+0

+1为好答案。 – Devart

0

我不会答应,这是更好的,它很可能会更糟 - 但它的不同的方法,即可能有帮助。给它一个旋转,看看它是否有用。

首先,我编译一个临时表,用于+ timelog.jobeditid +如果jobeditid是3,timelog.jobpart jobs.id的所有值相加时间:

SELECT j.id, tl.jobeditid, case when tl.jobeditid = 3 then tl.jobpart else '' end as [JobPart], (Sum(tl.duration)/60) as AdjTotalDuration 
INTO #t 
FROM Jobs J CROSS JOIN TimeLog tl 
GROUP BY j.id, tl.jobeditid, case when tl.jobeditid = 3 then tl.jobpart else '' end; 

现在我已经简化您的大量查询,以多种不同方式使用此临时表;我也感动最SELECT子句中的子查询到连接表中的from子句:

SELECT various, other, native, columns, c.Client, cg.name as [Category], s.fullname as Producer, 
    isnull(v.TotalAmount, 0) as voicecosts, (SELECT COUNT(*) FROM Scripts s WHERE s.jobid = j.id) AS numberofscriptscompleted, 
    isnull((SELECT SUM(AdjTotalDuration) FROM #t WHERE jobid = j.id),0) AS TotalTime, 
    t3a.AdjTotalDuration as PartAdd, 
    t3u.AdjTotalDuration as PartUpdate, 
    t3p.AdjTotalDuration as PartProduce, 
    t3m.AdjTotalDuration as PartAmend, 
    t4.AdjTotalDuration as EditProducerError, 
    t8.AdjTotalDuration as EditVoiceError, 
    t1.AdjTotalDuration as EditClientError, 
    t2.AdjTotalDuration as EditEntryError, 
    t5.AdjTotalDuration as EditPronunciation, 
    t6.AdjTotalDuration as EditRemixRequest, 
    t7.AdjTotalDuration as EditRevoiceRequest 
FROM Jobs j 
    join clients c on j.ClientID = c.ID 
    join categories cg on c.CategoryID = cg.ID 
    join staff s on j.ProducerID = s.ID 
    left join (select jobid, sum(amount) as TotalAmount from JobsVoiceWork group by jobid) v on j.id = v.jobid 
    left join (select * from #t where id = j.id and jobeditid = 3 and jobpart = 'Add') t3a 
    left join (select * from #t where id = j.id and jobeditid = 3 and jobpart = 'Update') t3u 
    left join (select * from #t where id = j.id and jobeditid = 3 and jobpart = 'Produce') t3p 
    left join (select * from #t where id = j.id and jobeditid = 3 and jobpart = 'Amend') t3m 
    left join (select * from #t where id = j.id and jobeditid = 4) t4 
    left join (select * from #t where id = j.id and jobeditid = 8) t8 
    left join (select * from #t where id = j.id and jobeditid = 1) t1 
    left join (select * from #t where id = j.id and jobeditid = 2) t2 
    left join (select * from #t where id = j.id and jobeditid = 5) t5 
    left join (select * from #t where id = j.id and jobeditid = 6) t6 
    left join (select * from #t where id = j.id and jobeditid = 7) t7; 

这是否提高它呢?它会变得更糟吗?

如果确实有所改善,请标记为答案。

问候 约翰

1

尝试这一个 -

SELECT various, other, native, columns, 
    c.client, 
    c2.name AS category, 
    s.fullname AS producer, 
    ISNULL(v.amount, 0) AS voicecosts, 
    s3.numberofscriptscompleted, 
    ISNULL(SUM(t.duration),0)/60 AS totaltime, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 3 AND t.jobpart = 'Add' THEN t.duration END),0)/60 AS PartAdd, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 3 AND t.jobpart = 'Update' THEN t.duration END),0)/60 AS PartUpdate, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 3 AND t.jobpart = 'Produce' THEN t.duration END),0)/60 AS PartProduce, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 3 AND t.jobpart = 'Amend' THEN t.duration END),0)/60 AS PartAmend, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 4 THEN t.duration END),0)/60 AS EditProducerError, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 8 THEN t.duration END),0)/60 AS EditVoiceError, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 1 THEN t.duration END),0)/60 AS EditClientError, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 2 THEN t.duration END),0)/60 AS EditEntryError, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 5 THEN t.duration END),0)/60 AS EditPronunciation, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 6 THEN t.duration END),0)/60 AS EditRemixRequest, 
    ISNULL(SUM(CASE WHEN t.jobeditid = 7 THEN t.duration END),0)/60 AS EditRevoiceRequest 
FROM Jobs j 
JOIN clients c ON j.id = c.clientid 
JOIN categories c2 ON c2.id = c.clientid 
JOIN staff s ON j.id = s.producerid 
LEFT JOIN (
    SELECT v.jobid, amount = SUM(amount) 
    FROM JobsVoiceWork v 
    GROUP BY v.jobid 
) v ON v.jobid = j.id 
JOIN (
    SELECT s.jobid, numberofscriptscompleted = COUNT(*) 
    FROM Scripts s 
    GROUP BY s.jobid 
) s3 ON s3.jobid = j.id 
LEFT JOIN Timelog t ON j.id = t.jobid 
--GROUP BY ...