2017-11-10 173 views
0

這是查詢的只是一部分,但它似乎是瓶頸:我需要幫助提高我的SQL查詢拉最近的一份文件數

SELECT CAST (CASE WHEN EXISTS 
      (SELECT 1 
       FROM dbo.CBDocument 
       WHERE (FirmId = R.FirmId) AND 
        (ContributionDate > DATEADD(m, -3, GETDATE())) AND 
        ((EntityTypeId = 2600 AND EntityId = P.IProductId) OR 
        (EntityTypeId = 2500 AND EntityId = M.IManagerId))) 
      THEN 1 ELSE 0 END AS BIT) AS HasRecentDocuments 

FROM dbo.CBIProduct P 
    JOIN dbo.CBIManager M ON P.IManagerId = M.IManagerId 
    JOIN dbo.CBIProductRating R ON P.IProductId = R.IProductId 
    JOIN dbo.CBIProductFirmDetail D ON (D.IProductId = P.IProductId) AND 
             (R.FirmId = D.FirmId) 

CROSS APPLY (SELECT TOP 1 RatingDate, IProductRatingId, FirmId 
      FROM dbo.CBIProductRating 
    WHERE (IProductId = P.IProductId) AND (FirmId = R.FirmId) 
    ORDER BY RatingDate DESC) AS RD 

WHERE (R.IProductRatingId = RD.IProductRatingId) AND (R.FirmId = RD.FirmId) 

有很多其他列的,我通常拉回需要CROSS APPLY和其他連接。我需要優化的位是case語句中的子查詢。這個子查詢需要3分鐘才能返回119k條記錄。我對SQL知之甚少,但必須有一種方法來提高效率。

查詢的要點就是返回一個標誌,如果相關產品已被添加到系統中的最後3個月內的任何文件。

編輯:我的數據庫託管在Azure中,數據庫優化顧問將不會連接到它。 Azure中有一個調優顧問組件,但它沒有提供任何建議。必須有更好的方法來查詢。

編輯:在試圖進一步簡化和確定的罪魁禍首,我又縮減到這個查詢:(而不是確定一個最近的文檔存在,它只是計算最近的文檔)。

SELECT D.FirmId, P.IProductId, 
     ,(SELECT COUNT(DocumentId) FROM dbo.CBDocument WHERE 
     (FirmId = D.FirmId) AND 
     (ContributionDate > DATEADD(m, -3, GETDATE())) AND 
     ((EntityTypeId = 2600 AND EntityId = P.IProductId) OR 
     (EntityTypeId = 2500 AND EntityId = M.IManagerId))) AS RecentDocCount 

FROM dbo.CBIProduct P 
FULL JOIN dbo.CBIProductFirmDetail D ON D.IProductId = P.IProductId 
JOIN dbo.CBIManager M ON M.IManagerId = P.IManagerId 

那運行3分53秒。

如果我聲明一個變量來存儲日期(DECLARE @Today DATE = GETDATE()) 並在查詢(DATEADD(m, -3, @Today))把變量到位GETDATE()的,它運行在12秒內。

是否與GETDATE一個已知的性能問題()?據我所知,我不能在視圖定義中使用該變量。

這是否對任何可能指向解決方案的東西閃耀光芒?我想我可以把整個事情變成一個存儲過程,但是我也必須調整應用程序代碼。

謝謝。

+0

https://docs.microsoft.com/en-us/sql/tools/dta/tutorial-database-engine-tuning-advisor做到這一點,你可能會很幸運,只需添加一些新索引 – Will

+1

謝謝@Will。我一直在那條路上。我無法將DTA連接到Azure中的數據庫,Azure的調優顧問不推薦任何內容。 –

+0

如果你可以阻止數據庫,並將其加載到本地sql服務器實例,你仍然可以嘗試。我相信,索引在蔚藍的天空中不會有所不同。 – Will

回答

1

根據的東西也可能是更快地使用左連接:

SELECT CAST(CASE when x.FirmId is not null THEN 1 ELSE 0 END AS BIT) AS HasRecentDocuments 

FROM dbo.CBIProduct P 
    JOIN dbo.CBIManager M ON P.IManagerId = M.IManagerId 
    JOIN dbo.CBIProductRating R ON P.IProductId = R.IProductId 
    JOIN dbo.CBIProductFirmDetail D ON (D.IProductId = P.IProductId) AND (R.FirmId = D.FirmId) 


LEFT JOIN dbo.CBDocument x ON x.FirmId = R.FirmId 
          AND x.ContributionDate > DATEADD(m, -3, GETDATE()) 
          AND ( (x.EntityTypeId = 2600 AND x.EntityId = P.IProductId) 
           OR (x.EntityTypeId = 2500 AND x.EntityId = M.IManagerId)) 

CROSS APPLY (SELECT TOP 1 RatingDate, IProductRatingId, FirmId 
      FROM dbo.CBIProductRating 
    WHERE (IProductId = P.IProductId) AND (FirmId = R.FirmId) 
    ORDER BY RatingDate DESC) AS RD 

WHERE (R.IProductRatingId = RD.IProductRatingId) AND (R.FirmId = RD.FirmId) 

它肯定看起來簡單。

+0

不得不稍微調整語法以阻止SQL Server抱怨,但這似乎不會產生相同的結果......它的運行速度更快,但我必須弄清楚爲什麼我要得到不同的結果集。此外,CASE似乎與NULL檢查有問題。我只得到0。 –

+0

嗯,有什麼變化(看到我錯過了'什麼時候'別的東西?) - 我不認爲案件有空檢查的問題 - 你是什麼意思? – Hogan

1

這是你索賠需要優化查詢:

SELECT CAST(CASE WHEN EXISTS (SELECT 1 
           FROM dbo.CBDocument d 
           WHERE (d.FirmId = R.FirmId) AND 
            (d.ContributionDate > DATEADD(m, -3, GETDATE())) AND 
            ((d.EntityTypeId = 2600 AND d.EntityId = P.IProductId) OR 
            (d.EntityTypeId = 2500 AND d.EntityId = M.IManagerId) 
            ) 
          ) 
    . . . 

我會相信你的判斷。我覺得措辭本查詢爲您提供更多的路徑優化:

SELECT CAST(CASE WHEN EXISTS (SELECT 1 
           FROM dbo.CBDocument d 
           WHERE d.FirmId = R.FirmId AND 
            d.ContributionDate > DATEADD(m, -3, GETDATE()) AND 
            d.EntityTypeId = 2600 AND d.EntityId = P.IProductId 
          ) OR 
         EXISTS (SELECT 1 
           FROM dbo.CBDocument d 
           WHERE d.FirmId = R.FirmId AND 
            d.ContributionDate > DATEADD(m, -3, GETDATE()) AND 
            d.EntityTypeId = 2500 AND d.EntityId = M.IManagerId 
          ) 
    . . . 

那麼你一定要在CBDocument(FirmId, EntityTypeId, EntityId, ContributionDate)的索引。

1

諸如correlated subqueriesfull outer join之類的操作相當昂貴,我建議您尋找替代方案。雖然我不熟悉你的數據模型和數據,我建議改變「從表」到CBIProductFirmDetail和我進一步假設內部聯接產品表,然後加入內對產品表管理器列表。如果該連接順序正確,則會消除一些外連接的開銷。

代替相關子查詢,以確定一個數,我建議你可以把它看成這是左加入了一個子查詢。

SELECT 
     d.FirmId 
    , p.IProductId 
    , COALESCE(Docs.RecentDocCount,0) RecentDocCount 
FROM dbo.CBIProductFirmDetail d 
JOIN dbo.CBIProduct p ON d.IProductId = p.IProductId 
JOIN dbo.CBIManager m ON p.IManagerId = m.IManagerId 
LEFT JOIN (
     SELECT 
      FirmId 
      , EntityId 
      , EntityTypeId 
      , COUNT(DocumentId) recentdoccount 
     FROM dbo.CBDocument 
     WHERE ContributionDate > DATEADD(m, -3, GETDATE()) 
     AND EntityTypeId IN (2500,2600) 
     GROUP BY 
      FirmId 
      , EntityId 
      , EntityTypeId 
) AS docs ON d.FirmId = docs.FirmId 
     AND (
       (docs.EntityTypeId = 2600 AND docs.EntityId = p.IProductId) 
      OR (docs.EntityTypeId = 2500 AND docs.EntityId = m.IManagerId) 
      ) 
; 

可能有益處除以子查詢過,以避免尷尬或以加盟,所以:

SELECT 
     d.FirmId 
    , p.IProductId 
    , COALESCE(d2500.DocCount,0) + COALESCE(d2600.DocCount,0) RecentDocCount 
FROM dbo.CBIProductFirmDetail d 
JOIN dbo.CBIProduct p ON d.IProductId = p.IProductId 
JOIN dbo.CBIManager m ON p.IManagerId = m.IManagerId 
LEFT JOIN (
     SELECT 
      FirmId 
      , EntityId 
      , COUNT(DocumentId) doccount 
     FROM dbo.CBDocument 
     WHERE ContributionDate > DATEADD(m, -3, GETDATE()) 
     AND EntityTypeId = 2500 
     GROUP BY 
      FirmId 
      , EntityId 
) AS d2500 ON d.FirmId = d2500.FirmId 
     AND m.IManagerId = d2500.EntityId 
LEFT JOIN (
     SELECT 
      FirmId 
      , EntityId 
      , COUNT(DocumentId) doccount 
     FROM dbo.CBDocument 
     WHERE ContributionDate > DATEADD(m, -3, GETDATE()) 
     AND EntityTypeId = 2600 
     GROUP BY 
      FirmId 
      , EntityId 
) AS d2600 ON d.FirmId = d2600.FirmId 
      AND p.IProductId = d2600.EntityId 
; 
+0

你的問題現在解決了嗎?你仍然有關於這個答案的問題嗎?要接受答案「[**點擊Tick **](https://ibb.co/ikqyO6)」以獲取更多信息,請參閱[help/accepting](https://stackoverflow.com/help/someone-answers) –