2017-01-02 92 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条记录。 –