2011-08-20 148 views
3

不知道從哪裏開始......但基本上我有一個報告表,一個帳戶表和一個帳戶歷史表。帳戶歷史記錄表中將包含零個或多個記錄,其中每條記錄是更改後帳戶取消標誌的狀態。 還有其他的東西正在進行,但基本上我希望返回帳戶明細數據,開始日期和結束日期的帳戶狀態取消位爲不同的列。SQL優化 - 從歷史表中獲取值從兩個​​不同的日期

這樣做的最好方法是什麼?

我有以下

理念)我應該做單獨的連接上歷史表,1每個日期下面的工作查詢?

我想我能做到這一點在三個單獨的查詢(GET開始快照,快照結束,與正常的報表查詢加盟每個快照)

別的東西嗎?

預期輸出:

AccountID, OtherData, StartDateCancelled, EndDateCancelled 

測試表:

DECLARE @Report TABLE (ReportID INT, StartDate DATETIME, EndDate DATETIME) 
DECLARE @ReportAccountDetail TABLE(ReportID INT, Accountid INT, Cancelled BIT) 
DECLARE @AccountHistory TABLE(AccountID INT, ModifiedDate DATETIME, Cancelled BIT) 

INSERT INTO @Report 
SELECT 1,'1/1/2011', '2/1/2011' 
-- 
INSERT INTO @ReportAccountDetail 
SELECT 1 AS ReportID, 1 AS AccountID, 0 AS Cancelled 
UNION 
SELECT 1,2,0 
UNION 
SELECT 1,3,1 
UNION 
SELECT 1,4,1 
-- 
INSERT INTO @AccountHistory 
SELECT 2 AS CustomerID, '1/2/2010' AS ModifiedDate, 1 AS Cancelled 
UNION-- 
SELECT 3, '2/1/2011', 1 
UNION-- 
SELECT 4, '1/1/2010', 1 
UNION 
SELECT 4, '2/1/2010', 0 
UNION 
SELECT 4, '2/1/2011', 1 

當前查詢:

SELECT Accountid, OtherData, 
MAX(CASE WHEN BeginRank = 1 THEN CASE WHEN BeginHistoryExists = 1 THEN HistoryCancelled ELSE DefaultCancel END ELSE NULL END) AS StartDateCancelled, 
MAX(CASE WHEN EndRank = 1 THEN CASE WHEN EndHistoryExists = 1 THEN HistoryCancelled ELSE DefaultCancel END ELSE NULL END) AS EndDateCancelled 
FROM 
(
SELECT c.Accountid, 
'OtherData' AS OtherData, 
--lots of other data 
ROW_NUMBER() OVER (PARTITION BY c.AccountID ORDER BY 
    CASE WHEN ch.ModifiedDate <= Report.StartDate THEN 1 ELSE 0 END DESC, ch.ModifiedDate desc) AS BeginRank, 
CASE WHEN ch.ModifiedDate <= Report.StartDate THEN 1 ELSE 0 END AS BeginHistoryExists, 
ROW_NUMBER() OVER (PARTITION BY c.AccountID ORDER BY 
    CASE WHEN ch.ModifiedDate <= Report.EndDate THEN 1 ELSE 0 END DESC, ch.ModifiedDate desc) AS EndRank, 
CASE WHEN ch.ModifiedDate <= Report.EndDate THEN 1 ELSE 0 END AS EndHistoryExists, 
    CAST(ch.Cancelled AS INT) AS HistoryCancelled, 
    0 AS DefaultCancel 
FROM 
@Report AS Report 
INNER JOIN @ReportAccountDetail AS C ON Report.ReportID = C.ReportID 
--Others joins related for data to return 
LEFT JOIN @AccountHistory AS CH ON CH.AccountID = C.AccountID 
WHERE Report.ReportID = 1 
) AS x 
GROUP BY AccountID, OtherData 

歡迎在寫上堆棧溢出問題。謝謝!

+0

只是一個建議,要求「最好的辦法」是比較模糊的。指定您當前的解決方案不可接受的內容很有幫助。 – Andomar

+0

感謝您的建議,我正在尋找最高效,但是我也對最乾淨的感興趣,即使它可能效率稍低。 – TrevDev

回答

4

ROW_NUMBER()經常讓我感到驚訝,並超出了我的期望。然而,在這種情況下,我會試圖使用相關的子查詢。至少,我會對他們的替代品進行測試。

注意:我也會使用真正的表格,真實的索引以及真實的假數據量。 (如果這是值得張貼這個問題,我假設,這是值得現實測試這個。)

SELECT 
    [Report].ReportID, 
    [Account].AccountID, 
    [Account].OtherData, 
    ISNULL((SELECT TOP 1 Cancelled FROM AccountHistory WHERE AccountID = [Account].AccountID AND ModifiedDate <= [Report].StartDate ORDER BY ModifiedDate DESC), 0) AS StartDateCancelled, 
    ISNULL((SELECT TOP 1 Cancelled FROM AccountHistory WHERE AccountID = [Account].AccountID AND ModifiedDate <= [Report].EndDate ORDER BY ModifiedDate DESC), 0) AS EndDateCancelled 
FROM 
    Report     AS [Report] 
LEFT JOIN 
    ReportAccountDetail AS [Account] 
    ON [Account].ReportID = [Report].ReportID 
ORDER BY 
    [Report].ReportID, 
    [Account].AccountID 

注:無論出於何種原因,我發現TOP 1ORDER BYMAX()更快。


在您提示考生而言,我稍微修改它只是使用的,而不是試圖使已存在的列上工作ISNULL。

我會在所有工作完成後加入「其他數據」,而不是在最內層的查詢中,以避免必須由所有「其他數據」進行分組。

WITH 
    HistoricData AS 
(
    SELECT 
    Report.ReportID, 
    c.Accountid, 
    c.OtherData, 
    ROW_NUMBER() OVER (PARTITION BY c.ReportID, c.AccountID ORDER BY CASE WHEN ch.ModifiedDate <= Report.StartDate THEN 1 ELSE 0 END DESC, ch.ModifiedDate DESC) AS BeginRank, 
    ROW_NUMBER() OVER (PARTITION BY c.ReportID, c.AccountID ORDER BY ch.ModifiedDate DESC) AS EndRank, 
    CH.Cancelled 
    FROM 
    @Report AS Report 
    INNER JOIN 
    @ReportAccountDetail AS C 
     ON Report.ReportID = C.ReportID 
    LEFT JOIN 
    @AccountHistory AS CH 
     ON CH.AccountID  = C.AccountID 
     AND CH.ModifiedDate <= Report.EndDate 
) 
, 
    FlattenedData AS 
(
    SELECT 
    ReportID, 
    Accountid, 
    OtherData, 
    ISNULL(MAX(CASE WHEN BeginRank = 1 THEN Cancelled END), 0) AS StartDateCancelled, 
    ISNULL(MAX(CASE WHEN EndRank = 1 THEN Cancelled END), 0) AS EndDateCancelled 
    FROM 
    [HistoricData] 
    GROUP BY 
    ReportID, 
    AccountID, 
    OtherData 
) 
SELECT 
    * 
FROM 
    [FlattenedData] 
LEFT JOIN 
    [OtherData] 
    ON Whatever = YouLike 
WHERE 
    [FlattenedData].ReportID = 1 


而且最終可能的版本...

WITH 
    ReportStartHistory AS 
(
    SELECT 
    * 
    FROM 
    (
    SELECT 
     [Report].ReportID, 
     ROW_NUMBER() OVER (PARTITION BY [Report].ReportID, [History].AccountID ORDER BY [History].ModifiedDate) AS SequenceID, 
     [History].* 
    FROM 
     Report     AS [Report] 
    INNER JOIN 
     AccountHistory   AS [History] 
     ON [History].ModifiedDate <= [Report].StartDate 
) 
    AS [data] 
    WHERE 
    SequenceID = 1 
) 
, 
    ReportEndHistory AS 
(
    SELECT 
    * 
    FROM 
    (
    SELECT 
     [Report].ReportID, 
     ROW_NUMBER() OVER (PARTITION BY [Report].ReportID, [History].AccountID ORDER BY [History].ModifiedDate) AS SequenceID, 
     [History].* 
    FROM 
     Report     AS [Report] 
    INNER JOIN 
     AccountHistory   AS [History] 
     ON [History].ModifiedDate <= [Report].EndDate 
) 
    AS [data] 
    WHERE 
    SequenceID = 1 
) 
SELECT 
    [Report].ReportID, 
    [Account].*, 
    ISNULL([ReportStartHistory].Cancelled, 0) AS StartDateCancelled, 
    ISNULL([ReportEndHistory].Cancelled, 0) AS EndDateCancelled 
FROM 
    Report      AS [Report] 
INNER JOIN 
    Account     AS [Account] 
LEFT JOIN 
    [ReportStartHistory] 
    ON [ReportStartHistory].ReportID = [Report].ReportID 
    AND [ReportStartHistory].AccountID = [Account].AccountID 
LEFT JOIN 
    [ReportEndHistory] 
    ON [ReportEndHistory].ReportID = [Report].ReportID 
    AND [ReportEndHistory].AccountID = [Account].AccountID 
+0

如果僅使用1個連接,則存在列是必需的。如果有人在報告中間取消,即使修改日期>開始日期,我們也會得到BeginRank = 1(對於EndRank中符合Criteria的記錄 – TrevDev