2011-01-12 54 views
2

我有一個總結的日子一上市就一直活躍的子查詢。這些列表有3個不同的優先級,我們要分別進行分析。子查詢工作正常,但我必須重複自己3次,我不禁感到有人可以指向我更優雅的方向。Sql Server的子查詢環流式

我在尋找沿

Declare @Priorities text = ['H','M','L'] 

Foreach(priority in priorities) 
    (SELECT SUM(
     CASE 
      WHEN(jlh_inner.StartDate IS NULL) THEN 0 
      WHEN(jlh_inner.EndDate IS NULL) THEN 
       DATEDIFF(dd, jlh_inner.StartDate, GETDATE()) 
      ELSE 
       DATEDIFF(dd, jlh_inner.StartDate, jlh_inner.EndDate) 
     END) 
    FROM ListingHistory jlh_inner 
    WHERE jlh_inner.JobTitleId = jlh.JobTitleId 
    AND jlh_inner.OfficeCode = rof.code 
    AND jlh_inner.Priority = priority) 

全部代碼線的東西現在的問題是:

BEGIN 
SELECT rof.location AS location, 
    jlh.TitleId AS TitleId, 
    jt.Title AS Title, 
    (SELECT SUM(
     CASE 
      WHEN(jlh_inner.StartDate IS NULL) THEN 0 
      WHEN(jlh_inner.EndDate IS NULL) THEN 
       DATEDIFF(dd, jlh_inner.StartDate, GETDATE()) 
      ELSE 
       DATEDIFF(dd, jlh_inner.StartDate, jlh_inner.EndDate) 
     END) 
    FROM ListingHistory jlh_inner 
    WHERE jlh_inner.JobTitleId = jlh.JobTitleId 
    AND jlh_inner.OfficeCode = rof.code 
    AND jlh_inner.Priority = 'H') AS HighPriorityDays, 
    (SELECT SUM(
     CASE 
      WHEN(jlh_inner.StartDate IS NULL) THEN 0 
      WHEN(jlh_inner.EndDate IS NULL) THEN 
       DATEDIFF(dd, jlh_inner.StartDate, GETDATE()) 
      ELSE 
       DATEDIFF(dd, jlh_inner.StartDate, jlh_inner.EndDate) 
     END) 
    FROM ListingHistory jlh_inner 
    WHERE jlh_inner.JobTitleId = jlh.JobTitleId 
    AND jlh_inner.OfficeCode = rof.code 
    AND jlh_inner.Priority = 'M') AS MediumPriorityDays, 
    (SELECT SUM(
     CASE 
      WHEN(jlh_inner.StartDate IS NULL) THEN 0 
      WHEN(jlh_inner.EndDate IS NULL) THEN 
       DATEDIFF(dd, jlh_inner.StartDate, GETDATE()) 
      ELSE 
       DATEDIFF(dd, jlh_inner.StartDate, jlh_inner.EndDate) 
     END) 
    FROM ListingHistory jlh_inner 
    WHERE jlh_inner.JobTitleId = jlh.JobTitleId 
    AND jlh_inner.OfficeCode = rof.code 
    AND jlh_inner.Priority = 'L') AS LowPriorityDays 

FROM Offices rof, 
    ListingHistory jlh, 
    JobTitle jt 

WHERE rof.code = jlh.OfficeCode 
AND jt.JobTitleID = jlh.JobTitleId 

GROUP BY rof.location, rof.code, jlh.TitleId, jt.Title 

ORDER BY location 

END

回答

2

你有你需要停止使用完全和相關子查詢是其中之一了一些非常糟糕的編碼實踐。他們是性能殺手,沒有理由永遠使用它。此外,您應該開始使用顯式連接,特別是因爲您使用的是SQL Server,並且外連接的不連接連接語法不僅不被棄用,而且實際上甚至會被破壞回到SQL Server 2000,並且隱式內連接和顯式連接通常會導致錯誤的結果,因此隱式聯接更難以維護,並且更有可能發生意外交叉聯接,並且當然將近20年過時。

看看這個代碼,你需要的東西:

SELECT rof.location AS location,  
jlh.TitleId AS TitleId,  
jt.Title AS Title,  
SUM(CASE WHEN(jlh.StartDate IS NULL and Priority <> 'H') THEN 0 
    WHEN(jlh.EndDate IS NULL AND Priority = 'H') 
    THEN DATEDIFF(dd, jlh.StartDate, GETDATE())    
    WHEN (jlh.EndDate IS NOT NULL AND Priority = 'H') THEN DATEDIFF(dd, jlh.StartDate, jlh.EndDate) END) AS HighPriorityDays,  
SUM(CASE WHEN(jlh.StartDate IS NULL and Priority <> 'M') THEN 0 
    WHEN(jlh.EndDate IS NULL AND Priority = 'M') 
    THEN DATEDIFF(dd, jlh.StartDate, GETDATE())    
    WHEN (jlh.EndDate IS NOT NULL AND Priority = 'M') THEN DATEDIFF(dd, jlh.StartDate, jlh.EndDate) END) AS MediumPriorityDays, 
SUM(CASE WHEN(jlh.StartDate IS NULL and Priority <> 'L') THEN 0 
    WHEN(jlh.EndDate IS NULL AND Priority = 'L') 
    THEN DATEDIFF(dd, jlh.StartDate, GETDATE())    
    WHEN (jlh.EndDate IS NOT NULL AND Priority = 'L') THEN DATEDIFF(dd, jlh.StartDate, jlh.EndDate) END) AS LowPriorityDays 
FROM Offices rof 
JOIN ListingHistory jlh 
    ON rof.code = jlh.OfficeCode 
JOIN  JobTitle jt 
    ON jt.JobTitleID = jlh.JobTitleId 
GROUP BY rof.location, rof.code, jlh.TitleId, jt.Title 
ORDER BY location 
+0

感謝這@HLGEM它工作出色,非常易於閱讀。 SQL不是我的強項,Sql Server我是全新的。我不知道隱式連接是如此的破裂。我不會再使用它們了。 – ChrisOPeterson 2011-01-12 19:54:08

2

而不是宣佈@priorities作爲文本的數組,使其成爲表格,然後將三個值插入@priorities表中,並將新現有的聚合查詢加入到新建的@priorities表中。這將使您的優先級表中的每行得到一個結果行,我認爲這是您真正的結果,對吧?

這是我怎麼會做表設置在MS SQL:

declare @priorities table (
priority char(1) 
) 

insert into @priorities (priority) values ('H') 
insert into @priorities (priority) values ('M') 
insert into @priorities (priority) values ('L') 

這將使您的最終實現包括這樣的事情:

SELECT jlh_inner.Priority, SUM(
     CASE 
      WHEN(jlh_inner.StartDate IS NULL) THEN 0 
      WHEN(jlh_inner.EndDate IS NULL) THEN 
       DATEDIFF(dd, jlh_inner.StartDate, GETDATE()) 
      ELSE 
       DATEDIFF(dd, jlh_inner.StartDate, jlh_inner.EndDate) 
     END) 
    FROM ListingHistory jlh_inner 
    INNER JOIN @priorities p on jlh_inner.Priority = p.priorities 
    WHERE jlh_inner.JobTitleId = jlh.JobTitleId 
    AND jlh_inner.OfficeCode = rof.code 

然後你的優先級字段添加到你已經有了GROUP BY子句,而且事情應該很好地彙總起來。

+0

我喜歡你是如何做到這一點。使用一張值表比我想象的更有意義。 – ChrisOPeterson 2011-01-12 19:54:53

+0

謝謝,很高興你喜歡它!這是我親自處理這個問題的方式,但我可能會在我的@priorities表中添加一個orderNumber字段,以便我可以非常輕鬆地對結果進行排序。 – 2011-01-12 19:56:33

2

同意HLGEM,使用聯接語法是比較合適的。它也適用於OUTER APPLY語法。這實現了你想要的代碼而不用重複訪問表3次。

SELECT rof.location AS location, 
    jlh.TitleId AS TitleId, 
    jt.Title AS Title, 
    sum(case when PD.Priority='H' then PD.PriorityDays end) as HighPriorityDays, 
    sum(case when PD.Priority='M' then PD.PriorityDays end) as MediumPriorityDays, 
    sum(case when PD.Priority='L' then PD.PriorityDays end) as LowPriorityDays 
FROM Offices rof 
inner join ListingHistory jlh on rof.code = jlh.OfficeCode 
inner join JobTitle jt on jt.JobTitleID = jlh.JobTitleId 
outer apply 
    (SELECT jlh_inner.Priority, SUM(
     CASE 
      WHEN(jlh_inner.StartDate IS NULL) THEN 0 
      WHEN(jlh_inner.EndDate IS NULL) THEN 
       DATEDIFF(dd, jlh_inner.StartDate, GETDATE()) 
      ELSE 
       DATEDIFF(dd, jlh_inner.StartDate, jlh_inner.EndDate) 
     END) as PriorityDays 
    FROM ListingHistory jlh_inner 
    WHERE jlh_inner.JobTitleId = jlh.JobTitleId 
    AND jlh_inner.OfficeCode = rof.code 
    AND jlh_inner.Priority in ('H','L','M') 
    GROUP BY jlh_inner.Priority) PD 
GROUP BY rof.location, rof.code, jlh.TitleId, jt.Title 
ORDER BY location 
1

下面我將如何使用公用表表達式(代表分組適當的總結)重構,並明確連接:

WITH Summaries AS (
    SELECT jlh.JobTitleId 
      ,jlh.OfficeCode 
      ,jlh.Priority 
      ,SUM(
      CASE 
       WHEN(jlh.StartDate IS NULL) THEN 0 
       WHEN(jlh.EndDate IS NULL) THEN 
        DATEDIFF(dd, jlh.StartDate, GETDATE()) 
       ELSE 
        DATEDIFF(dd, jlh.StartDate, jlh.EndDate) 
      END 
     ) AS DayCount 
    FROM ListingHistory jlh 
    GROUP BY jlh.JobTitleId 
      ,jlh.OfficeCode 
      ,jlh.Priority 
) 
SELECT rof.location AS location 
    ,jlh.TitleId AS TitleId 
    ,jt.Title AS Title 
    ,s_h.DayCount AS HighPriorityDays 
    ,s_m.DayCount AS MediumPriorityDays 
    ,s_l.DayCount AS LowPriorityDays 
FROM Offices rof 
INNER JOIN ListingHistory jlh 
    ON rof.code = jlh.OfficeCode 
INNER JOIN JobTitle jt 
    ON jt.JobTitleID = jlh.JobTitleId 
LEFT JOIN Summaries s_h 
    ON s_h.JobTitleId = jlh.JobTitleId 
    AND s_h.OfficeCode = rof.code 
    AND s_h.Priority = 'H' 
LEFT JOIN Summaries s_m 
    ON s_m.JobTitleId = jlh.JobTitleId 
    AND s_m.OfficeCode = rof.code 
    AND s_m.Priority = 'M' 
LEFT JOIN Summaries s_l 
    ON s_l.JobTitleId = jlh.JobTitleId 
    AND s_l.OfficeCode = rof.code 
    AND s_l.Priority = 'L' 
GROUP BY rof.location, rof.code, jlh.TitleId, jt.Title 
ORDER BY location