2013-07-11 204 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 ...