2015-12-10 57 views
6

我有一個歷史記錄表,它捕獲某個對象的更新,並且除了其他信息外,還捕獲此更新發生的時間。我想要做的是SELECTMIN(LogDate)對應於某個ActionTaken列。在SQL Server中選擇一組日期

更具體地,歷史表可能有其他(最近)行,其中ActionTaken = 1,但我想捕捉的日期ActionTaken成爲 1.

例子:

SELECT MIN(LogDate) AS FirstActionDate 
FROM HistoryTable 
WHERE ID = 123 
    AND FirstActionTaken = 1 

SELECT MIN(LogDate) AS SecondActionDate 
FROM HistoryTable 
WHERE ID = 123 
    AND SecondActionTaken = 1 

SELECT MIN(LogDate) AS ThirdActionDate 
FROM HistoryTable 
WHERE ID = 123 
    AND ThirdActionTaken = 1 

這種運作良好,並且我收到正確的日期沒有問題。當我遇到麻煩,然後將從此組selectMAX(LogDate)

SELECT MAX(LogDate) AS LastActionDate 
FROM HistoryTable 
WHERE ID = 123 
    AND LogDate IN 
    (
      ( SELECT MIN(LogDate) AS FirstActionDate 
       FROM HistoryTable 
       WHERE ID = 123 
        AND FirstActionTaken = 1 ), 

      ( SELECT MIN(LogDate) AS SecondActionDate 
       FROM HistoryTable 
       WHERE ID = 123 
        AND SecondActionTaken = 1 ), 

      ( SELECT MIN(LogDate) AS ThirdActionDate 
       FROM HistoryTable 
       WHERE ID = 123 
        AND ThirdActionTaken = 1 ) 
    ) 

這也適用,但我討厭做這種方式。我可以將之前的語句存入變量,並且只需從SELECT MAX()開始;它肯定會更具可讀性,但是對於這個查詢來說,JOIN語法是什麼樣的?

是否有辦法將前三個SELECT語句合併爲一個返回所有三個日期並且不是不可讀的亂碼?

我怎麼能搶最近LogDate(作爲一個單獨的列)從這個結果集並沒有(看似不必要的)重複SELECT報表?

編輯:

這裏有幾個環節,我在有關發現已到目前爲止給出的答案:

何這些將幫助其他人尋找類似問題的解決方案!

+0

使用一個聯盟,然後你可以使用IN – JamieD77

回答

2

EDIT 2

基於新的信息

可以從OP自己的答案被收集(如何定義的最新行動日期),查詢可以被進一步簡化爲簡單:

select coalesce(
     min(case when ThirdActionTaken = 1 then LogDate end), 
     min(case when SecondActionTaken = 1 then LogDate end), 
     min(case when FirstActionTaken = 1 then LogDate end) 
     ) as LastActionDate 
    from HistoryTable 
where id = 123 

逆透視也可以使用:

select max(ActionDate) 
    from (select min(case when FirstActionTaken = 1 then LogDate end) as FirstActionDate, 
       min(case when SecondActionTaken = 1 then LogDate end) as SecondActionDate, 
       min(case when ThirdActionTaken = 1 then LogDate end) as ThirdActionDate 
      from HistoryTable 
      where id = 123) t 
unpivot (ActionDate for ActionDates in (FirstActionDate, SecondActionDate, ThirdActionDate)) unpvt 

編輯:簡短的說明

這個答案是非常相似,戈登在其使用條件聚合得到的最少3個日期在一個查詢。

所以查詢的以下部分:

select min(case when FirstActionTaken = 1 then LogDate end) as FirstActionDate, 
     min(case when SecondActionTaken = 1 then LogDate end) as SecondActionDate, 
     min(case when ThirdActionTaken = 1 then LogDate end) as ThirdActionDate 
    from HistoryTable 
where id = 123 

...可能會返回類似...

FirstActionDate SecondActionDate ThirdActionDate 
--------------- ---------------- --------------- 
    2015-01-01   2015-12-01   (null) 

然後,unpivot條款是什麼 「unpivots」 的3列進結果集有3行,而是單列:

ActionDate 
---------- 
2015-01-01 
2015-12-01 
    (null) 

一旦結果以這種格式顯示,那麼可以應用一個簡單的max聚合函數(select max(ActionDate))來獲取3行的最大值。

+0

這很好,感覺比我自己的嘗試更好:)你介意添加一些關於這裏發生的事情的解釋嗎?特別是關於'UNPIVOT'。感謝你的回答! – levelonehuman

+0

非常好,謝謝你對答案的更新 - 並且對於沒有指定開始的道歉表示歉意! – levelonehuman

1

您可以使用UNION爲您的IN語句加入3個查詢。

喜歡的東西

SELECT 
    MAX(ht1.LogDate) AS LastActionDate 
FROM 
    HistoryTable ht1 
WHERE 
    ht1.ID = 123 
    AND ht1.LogDate IN (SELECT 
         MIN(LogDate) AS FirstActionDate 
        FROM 
         HistoryTable ht2 
        WHERE 
         ht2.ID = ht1.ID 
         AND ht2.FirstActionTaken = 1 
        UNION 
        SELECT 
         MIN(LogDate) AS FirstActionDate 
        FROM 
         HistoryTable ht2 
        WHERE 
         ht2.ID = ht1.ID 
         AND ht2.SecondActionTaken = 1 
        UNION 
        SELECT 
         MIN(LogDate) AS FirstActionDate 
        FROM 
         HistoryTable ht2 
        WHERE 
         ht2.ID = ht1.ID 
         AND ht2.ThirdActionTaken = 1) 
3

這將是一個標準化的數據結構更簡單。這是一種使用條件聚合來計算三個最小日期的方法。然後取最大的值:

SELECT v.dt 
FROM (SELECT MIN(CASE WHEN FirstActionTaken = 1 THEN LogDate END) AS d1, 
      MIN(CASE WHEN SecondActionTaken = 1 THEN LogDate END) AS d2, 
      MIN(CASE WHEN ThirdActionTaken = 1 THEN LogDate END) AS d3  
    FROM HistoryTable 
    WHERE ID = 123 
    ) ht OUTER APPLY 
    (SELECT MAX(dt) as dt 
    FROM (VALUES (d1), (d2), (d3)) v(dt) 
    ) v; 
0

您可以在不使用PIVOT的情況下解決此問題。下面的代碼擴展您最初的代碼來存儲最小值爲變量,然後計算它們之間的最大值:

DECLARE @FirstActionDate DATETIME = NULL; 
DECLARE @SecondActionDate DATETIME = NULL; 
DECLARE @ThirdActionDate DATETIME = NULL; 
DECLARE @LastActionDate DATETIME = NULL; 

SELECT @FirstActionDate = MIN(LogDate) 
FROM HistoryTable 
WHERE ID = 123 
    AND FirstActionTaken = 1 

SELECT @SecondActionDate = MIN(LogDate) 
FROM HistoryTable 
WHERE ID = 123 
    AND SecondActionTaken = 1 

SELECT @ThirdActionDate = MIN(LogDate) 
FROM HistoryTable 
WHERE ID = 123 
    AND ThirdActionTaken = 1 

-- calculate @LastActionDate as the greater from @FirstActionDate, @SecondActionDate and @ThirdActionDate. 
SET @LastActionDate = @FirstActionDate; 
IF (@SecondActionDate > @LastActionDate) SET @LastActionDate = @SecondActionDate; 
IF (@ThirdActionDate > @LastActionDate) SET @LastActionDate = @ThirdActionDate; 

SELECT @FirstActionDate AS [FirstActionDate] 
, @SecondActionDate  AS [SecondActionDate] 
, @ThirdActionDate  AS [ThirdActionDate] 
, @LastActionDate  AS [LastActionDate] 

如果你想絕對的最後一個動作日期,你可以改變原來的代碼只是一個單一的聲明如下:

SELECT MAX(LogDate) AS [LastActionDate] 
, MIN(CASE WHEN FirstActionTaken = 1 THEN LogDate ELSE NULL END) AS [FirstActionDate] 
, MIN(CASE WHEN SecondActionTaken = 1 THEN LogDate ELSE NULL END) AS [SecondActionDate] 
, MIN(CASE WHEN ThirdActionTaken = 1 THEN LogDate ELSE NULL END) AS [ThirdActionDate] 
FROM HistoryTable 
WHERE ID = 123 
0

我自己在重構最後SELECT聲明嘗試:

SELECT MIN(ht2.LogDate) AS FirstActionDate, 
     MIN(ht3.LogDate) AS SecondActionDate, 
     MIN(ht4.LogDate) AS ThirdActionDate, 
     COALESCE (
      MIN(ht4.LogDate), 
      MIN(ht3.LogDate), 
      MIN(ht2.LogDate) 
     ) AS LastActionDate 
FROM HistoryTable ht 
    INNER JOIN HistoryTable ht2 
     ON ht2.ID = ht.ID AND ht2.FirstActionTaken = 1 
    INNER JOIN HistoryTable ht3 
     ON ht3.ID = ht.ID AND ht3.SecondActionTaken = 1 
    INNER JOIN HistoryTable ht4 
     ON ht4.ID = ht.ID AND ht4.ThirdActionTaken = 1 
WHERE ht.ID = 123 
GROUP BY ht.ID 

JOINSHistoryTable每個xActionTaken列和SELECTS各自MIN(LogDate)。然後,我們向後走過結果(ThirdAction,SecondAction, FirstAction),然後返回我們發現的第一個爲LastActionTaken

不可否認,這有點凌亂,但我認爲這將是很好的顯示另一種替代檢索相同的數據。

另外值得注意的性能:

運行我的回答對UNPIVOTOUTER APPLY方法後,SSMS Execution Plan表明UNPIVOTOUTER APPLY大致相當(以約。每個執行時間的50%)。

將我的方法與其中任何一種進行比較時,我的方法大約需要。執行時間爲88%,其中UNPIVOT/OUTER APPLY僅取12% - 因此UNPIVOTOUTER APPLY執行速度要快得多(至少在本例中)。

我的方法需要這麼長的時間的原因是SQL每次我回到它時進行HistoryTable的表掃描,總共有4次掃描。使用其他兩種方法,此操作僅執行一次。

+0

您的問題從未指定'ThirdActionDate> SecondActionDate> FirstActionDate',但您的答案意味着這總是如此。既然如此,你不需要任何unpivot,outer apply或者join。該查詢只需要使用合併的條件聚合。我編輯了我的答案。這應該會給你最好的表現。 – sstan

相關問題