簡體   English   中英

如何使用重復的子查詢優化SQL Server查詢

[英]How to optimize a SQL Server query with repetitive sub queries

我受制於舊式數據庫結構,需要一些統計結果。 以下查詢有效,但效率低下且緩慢...

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

我已經顯示了查詢的簡化版本,但是我包含了重復的子查詢以清楚地證明效率低下。 我嘗試了各種表連接方案,但是無法提高性能。

看起來應該可以改善。 有辦法嗎?

您可以使用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

試試這個-

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 ...

我不會保證這會更好,也可能會更糟-但采用另一種方法可能會有所幫助。 旋轉一下,看看它是否有用。

首先,我正在編譯一個臨時表,將job.id + timelog.jobeditid的所有值的持續時間相加,如果jobeditid為3,則為timelog.jobpart:

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; 

這會完全改善嗎? 它會使情況變得更糟嗎?

如果確實有改善,請標記為答案。

問候約翰

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM