2017-01-02 95 views
1

我的應用程序將單行數據分割成不同的行塊,這些塊總是按照startdate的排序順序排列。合併sql行組

其中rowpart = 0是開始,rowpart = 2總是結束 rowpart = 1是中間部分,可以重複n次。

我需要在這樣的形式返回行像rowpart = 0的開始日期和rowpart = 2的結束日期(如果存在的話,否則返回rowpart結束日期)

  • Rowpart = 0是新行塊的開始
  • Rowpart = 2總是大塊

結塊可以在不同的日期被傳播的端部。

+-----+-------------------------+-------------------------+----------+ 
| Id |  startdate  |   enddate   | rowpart | 
+-----+-------------------------+-------------------------+----------+ 
| 100 | 2016-11-30 00:00:00.000 | 2016-11-30 01:00:00.000 |  0 | 
| 100 | 2016-11-30 02:00:00.000 | 2016-11-30 03:00:00.000 |  1 | 
| 100 | 2016-11-30 10:00:00.000 | 2016-12-01 00:00:00.000 |  0 | 
| 100 | 2016-12-01 02:00:00.000 | 2016-12-01 02:30:00.000 |  1 | 
| 100 | 2016-12-01 10:00:00.000 | 2016-12-01 10:30:00.000 |  1 | 
| 100 | 2016-12-01 16:00:00.000 | 2016-12-01 16:30:00.000 |  2 | 
| 101 | 2016-12-11 10:00:00.000 | 2016-12-11 10:30:00.000 |  0 | 
+-----+-------------------------+-------------------------+----------+ 

所以上面的表格應返回:

+-----+-------------------------+-------------------------+ 
| Id |  startdate  |   enddate   | 
+-----+-------------------------+-------------------------+ 
| 100 | 2016-11-30 00:00:00.000 | 2016-11-30 03:00:00.000 | 
| 100 | 2016-12-30 10:00:00.000 | 2016-12-01 16:30:00.000 | 
| 101 | 2016-12-11 10:00:00.000 | 2016-12-11 10:30:00.000 | 
+-----+-------------------------+-------------------------+ 

任何幫助,將不勝感激

+0

根據SO你應該回答一些問題你問的問題..如果它可以幫助你的任何答案..do upvote ..happy編碼 – mohan111

+0

我沒有回覆下面發佈的答案,我仍然試圖解決這個。 –

+0

哪個版本的SQL Server? – MatBailie

回答

0

這應該工作:

;WITH temp 
AS 
(
SELECT Id, startdate,enddate,rowpart, 
     --Find out First Record 
     CASE WHEN rowpart=0 
      THEN 1 
      ELSE 0 
     END AS is_first, 
     --Find out Last Record, Check if next rowpart is 0 or NULL: 
     CASE WHEN COALESCE(LEAD(rowpart) OVER (ORDER BY Id, startdate),0) = 0 --Check if next rowpart is 0 or NULL 
      THEN 1 
      ELSE 0 
     END AS is_last 
FROM @tab 
) 

SELECT DISTINCT 
     Id, 
     CASE WHEN is_first = 1 
      THEN startdate 
      ELSE LAG(startdate) OVER (ORDER BY Id, startdate) 
     END AS startdate, 
     CASE WHEN is_last = 1 
      THEN enddate 
      ELSE LEAD(enddate) OVER (ORDER BY Id, startdate) 
     END AS enddate 
FROM temp 
WHERE is_first = 1 OR is_last = 1 
ORDER BY Id, startdate 

什麼,我試圖在這裏做的:在CTE我內心迎來首個和每個序列中的最後一個記錄。如果rowpart = 0 - >它是第一條記錄。如果下一條記錄爲空或下一條記錄的rowpart爲0,則我們有最後一條記錄。

所以當查詢CTE時,我們可以消除「中間的記錄」。剩下的是每個序列1或2個記錄(第一個和最後一個,在某些情況下,這是相同的記錄)。

然後我們將startdate替換爲序列的第一條記錄的開始日期和enddate以及序列的最後一條記錄的結束日期。

使用DISTINCT消除重複值,您將獲得所需的輸出。

這是一個骯髒的一片SQL的,但至少它的工作原理;-)

如果你不知道SQL服務器LEADLAG函數來訪問一個或下一個行值檢查了這一點:http://blog.sqlauthority.com/2013/09/22/sql-server-how-to-access-the-previous-row-and-next-row-value-in-select-statement/

+0

「不完整」序列有問題。如果對於單個id,行部分是'0,1,1,2,2,0,1,2,0',那麼'第二序列'和'第四序列'只有一個長條目,但是你使用'LEAD(enddate)'假定至少有兩個條目。在這種情況下,您的邏輯會從下一個序列的第一行獲取結束日期。實際上,只有一行的序列可能只發生在'id'的最後一個條目(參見OP的'id'101的最後一行),但它「可能」在「mid-stream」中是可能的...... – MatBailie

+0

不,因爲LEAD(rowpart)是0(2. sequence)或null(4),所以「單行序列」將得到is_last = 1。序列)!! – CeOnSql

+1

非常感謝@CeOnSql。考慮只有rowpart = 0,不分組或取決於日期。這正是我想要的。萬分感謝。 –

0

檢查。使用CTE和連接:

 with CTE as 
     (
      select distinct *, 
      CASE WHEN COALESCE(LEAD(rowpart) OVER (ORDER BY Id, startdate),0) = 0 
      THEN 1 
      ELSE 0 
      end as RN2 
      from #table 
     )   
     select distinct bb.id,bb.startdate,aa.enddate from 
     (
      select C2.*,ROW_NUMBER()OVER( ORDER BY id, startdate) RN3    
      from CTE C2 where RN2= 1 
     ) aa 
      join 
      ( 
      select distinct *, 
      ROW_NUMBER()OVER( ORDER BY id, startdate) RN3 
       from CTE c1 where rowpart=0 
     ) bb on aa.RN3=bb.RN3 

輸出:

enter image description here

+0

這將無法正常工作,因爲id(例如id = 100)會重複。一個id可以有多個塊。所以這返回多個值,因爲它加入ID –

+0

檢查代碼更新。 –

+0

先生非常感謝。問題是b.rowpart = 2,它總是加入到最後一行。有沒有rowpart = 2的情況。 –

0

看起來像一個簡單的Group by是所有你需要

試試這個

select Id,min(startdate),max(enddate) 
From yourtable 
Group by Id,cast(startdate as date) 
+0

謝謝你,你的查詢很有魅力。唯一的問題是如果塊分佈在不同的日期。 –

+0

@NavedMir - 添加有問題的場景。您使用的是哪個版本的sql server –

+0

對不起,我的sql server 2012 –

0
Select 
Id, 
startdate, 
enddate 
from (
    select Id, 
startdate, 
enddate,ROW_NUMBER()OVER(PARTITION BY CONVERT(DATE,startdate) ORDER BY startdate DESC )RN from @Table1 
    GROUP BY Id, startdate, enddate)T 
WHERE T.RN = 1 
+0

這不起作用,因爲它按startdate進行分區,並且只返回第1行,這對於enddate部分不正確 –

+0

然後在檢查分區中也添加結束日期但它給出了確切的結果集你已發佈@NavedMir – mohan111

+0

先生,請檢查結果中的endDate列 –

0
WITH 
    your_table_lead AS 
(
    SELECT 
     your_table.*, 
     LAG(rowpart, 1, 2) OVER (PARTITION BY id 
             ORDER BY startdate) AS last_rowpart, 
     LEAD(rowpart, 1, 0) OVER (PARTITION BY id 
             ORDER BY startdate) AS next_rowpart 
    FROM 
     your_table 
), 
    filtered_sorted AS 
(
    SELECT 
     *, 
     ROW_NUMBER() OVER (PARTITION BY id 
           ORDER BY startdate) AS id_seq_num 
    FROM 
     your_table_lead 
    WHERE 
       rowpart IN (0, 2) 
     OR next_rowpart = 0 
     OR last_rowpart = 2 
) 
SELECT 
    id, 
    MIN(startdate), 
    MAX(enddate) 
FROM 
    filtered_sorted 
GROUP BY 
    id, 
    id_seq_num - CASE rowpart WHEN 2 THEN 1 ELSE rowpart END 

我我的手機上,以便道歉錯別字等

的第一步只是儘量過濾掉一切,除了每個「組」的第一個和最後一個條目。如果rowpart爲0或2,則包含該行,或者如果Next Row的rowpart爲0,則包含該行(如果沒有下一行,則使用0)。

然後,'技巧'是找到一種方法來組合'對'。

如果我們有一個0,2,0,1,0,2,2,0的序列,那麼我們想要將它們分組,如a,a,b,b,c,c,d,e

這可以通過將所有的2變成1來完成,從ROW_NUMBER()中扣除值。

0,2,0,1,0,2,2,0 =>0,1,0,1,0,1,1,0

1,2,3,4,5,6,7,8 - 0,1,0,1,0,1,1,0 =>1,1,3,3,5,5,6,8

所以,現在我們有5個不同的 '基團',在其上我們可以應用MIN()和MAX()。

+0

您的查詢返回錯誤的數據/多個條目用於ID 100條目。 –

+0

@Bhosdale先生似乎對我有用。究竟它給出了什麼結果呢? – MatBailie

+0

你的代碼返回4條記錄。 –