2013-02-12 64 views
6

如何将行与其他行中的其他行之间的不同时间几乎相同?如果值几乎相同,如何按DateTime对行进行分组?

例如:

GUID - No - sDateTime   - eDateTime   - Name 
------------------------------------------------------------------  
0000 - 01 - 2013-02-02 08:00:00 - 2013-02-02 08:40:00 - A 
0000 - 02 - 2013-02-02 08:45:00 - 2013-02-02 09:45:00 - A 
0000 - 03 - 2013-02-02 11:30:00 - 2013-02-02 12:00:00 - A 
0000 - 04 - 2013-02-02 09:55:00 - 2013-02-02 11:00:00 - A 
0000 - 05 - 2013-02-02 11:05:00 - 2013-02-02 12:30:00 - B 

我怎样才能sDateTimeeDateTime行与其他行,如果不超过无异10分钟呢?

GUID - No  - SDateTime   - EDateTime (From other row) - Name 
---------------------------------------------------------------------------- 
0000 -01,02,04 - 2013-02-02 08:00:00 - **2013-02-02 11:00:00** - A 
0000 - 03  - 2013-02-02 11:30:00 - 2013-02-02 12:00:00  - A 
0000 - 05  - 2013-02-02 11:05:00 - 2013-02-02 12:30:00  - B 
+0

您正在使用哪种SQL Server版本? – 2013-02-12 21:59:30

+0

@NitinMidha SQL Server 2008 Express Edition – 2013-02-12 22:01:20

+0

另外为什么05不与01,02和04组合?我认为它符合10分钟的标准,如果我理解正确。 – 2013-02-12 22:03:15

回答

3

这里没有游标,但这仍然是循环使用递归CTE找出哪些行应该组合在一起。临时表#T用于容纳将行连接到彼此的ID。它可以在没有临时表的情况下完成,但出于性能方面的原因,最好使用临时表来完成,而不是使用另一个CTE作为递归CTE的源。

在最后一个查询中,我使用for xml path技巧连接了来自No的值。这意味着CTE C被再次使用,所以它将被执行两次。您可能需要将该CTE转换为临时表以避免这种情况。

SQL Fiddle

MS SQL Server 2008的架构设置

create table YourTable 
(
    GUID varchar(4), 
    No varchar(2), 
    sDateTime datetime, 
    eDateTime datetime, 
    Name varchar(1) 
); 

insert into YourTable values 
('0000', '01', '2013-02-02 08:00:00', '2013-02-02 08:40:00', 'A'), 
('0000', '02', '2013-02-02 08:45:00', '2013-02-02 09:45:00', 'A'), 
('0000', '03', '2013-02-02 11:30:00', '2013-02-02 12:00:00', 'A'), 
('0000', '04', '2013-02-02 09:55:00', '2013-02-02 11:00:00', 'A'), 
('0000', '05', '2013-02-02 11:05:00', '2013-02-02 12:30:00', 'B'); 

查询1

create table #T 
(
    ID int, 
    GUID varchar(4), 
    No varchar(2), 
    sDateTime datetime, 
    eDateTime datetime, 
    Name varchar(1), 
    primary key(ID, GUID, Name) 
); 

insert into #T(ID, GUID, No, sDateTime, eDateTime, Name) 
select row_number() over(partition by GUID, Name order by sDateTime), 
     GUID, No, sDateTime, eDateTime, Name 
from YourTable; 

with C as 
(
    select T.ID, T.GUID, T.No, T.sDateTime, T.eDateTime, T.Name, 1 as Grp 
    from #T as T 
    where T.ID = 1 
    union all 
    select T.ID, T.GUID, T.No, T.sDateTime, T.eDateTime, T.Name, 
     C.Grp + case when datediff(minute, C.eDateTime, T.sDateTime) > 10 
        then 1 
        else 0 
       end 
    from #T as T 
    inner join C 
     on T.ID = C.ID + 1 and 
     T.Name = C.Name and 
     T.GUID = C.GUID 
) 
select C.GUID, 
     (
     select ','+C2.No 
     from C as C2 
     where C.GUID = C2.GUID and 
      C.Name = C2.Name and 
      C.Grp = C2.Grp 
     order by C2.No 
     for xml path(''), type 
     ).value('substring(text()[1], 2)', 'varchar(max)') as No, 
     min(C.sDateTime) as sDateTime, 
     max(C.eDateTime) as eDateTime, 
     C.Name 
from C 
group by C.GUID, C.Name, C.Grp 

drop table #T; 

Results

| GUID |  NO |      SDATETIME |      EDATETIME | NAME | 
---------------------------------------------------------------------------------------------- 
| 0000 | 01,02,04 | February, 02 2013 08:00:00+0000 | February, 02 2013 11:00:00+0000 | A | 
| 0000 |  03 | February, 02 2013 11:30:00+0000 | February, 02 2013 12:00:00+0000 | A | 
| 0000 |  05 | February, 02 2013 11:05:00+0000 | February, 02 2013 12:30:00+0000 | B | 

仅供参考,SQL Server 2012版本。

with CMinDiff as 
(
    select GUID, No, sDateTime, eDateTime, Name, 
     case when datediff(minute, 
          coalesce(lag(eDateTime) over(partition by GUID, Name 
                 order by eDateTime), 
            sDateTime), 
          sDateTime) <= 10 
      then 0 
      else 1 
     end as MinDiff 
    from YourTable 
), CSumMinDiff as 
(
    select GUID, No, sDateTime, eDateTime, Name, 
     sum(MinDiff) over(partition by GUID, Name 
          order by sDateTime 
          rows between unbounded preceding and current row) as Grp 
    from CMinDiff 
) 
select C.GUID, 
     (
     select ','+C2.No 
     from CSumMinDiff as C2 
     where C.GUID = C2.GUID and 
      C.Name = C2.Name and 
      C.Grp = C2.Grp 
     order by C2.No 
     for xml path(''), type 
     ).value('substring(text()[1], 2)', 'varchar(max)') as No, 
     min(C.sDateTime) as sDateTime, 
     max(C.eDateTime) as eDateTime, 
     C.Name 
from CSumMinDiff as C 
group by C.GUID, C.Name, C.Grp 
+0

非常感谢你 – 2013-02-14 09:07:27

1

我无法不光标执行此,也许有人聪明能想出不包括游标的解决方案,但是这是我有:

CREATE TABLE MyTable 
(
    [GUID] VARCHAR(4), 
    [No] VARCHAR(2), 
    [sDateTime] DATETIME, 
    [eDateTime] DATETIME, 
    [Name] VARCHAR(1) 
) 

INSERT INTO [MyTable] 
([GUID], [No], [sDateTime], [eDateTime], [Name]) 
VALUES 
('0000', '01', '2013-02-02 08:00:00', '2013-02-02 08:40:00', 'A'), 
('0000', '02', '2013-02-02 08:45:00', '2013-02-02 09:45:00', 'A'), 
('0000', '03', '2013-02-02 11:30:00', '2013-02-02 12:00:00', 'A'), 
('0000', '04', '2013-02-02 09:55:00', '2013-02-02 11:00:00', 'A'), 
('0000', '05', '2013-02-02 11:05:00', '2013-02-02 12:30:00', 'B') 

DECLARE @MyTable TABLE 
(
    [GUID] VARCHAR(4), 
    [No] VARCHAR(100), 
    [sDateTime] DATETIME, 
    [eDateTime] VARCHAR(100), 
    [Name] VARCHAR(1) 
) 

DECLARE [MyCursor] CURSOR FOR 
SELECT [GUID], [No], [sDateTime], [eDateTime], [Name] 
FROM [MyTable] 
ORDER BY [Name], [sDateTime]; 

DECLARE @GUID AS VARCHAR(4) 
DECLARE @No AS VARCHAR(2) 
DECLARE @sDateTime AS DATETIME 
DECLARE @eDateTime AS DATETIME 
DECLARE @Name AS VARCHAR(1) 
DECLARE @OldName AS VARCHAR(1) 
SET @OldName = '' 
DECLARE @MergedNo AS VARCHAR(100) 
SET @MergedNo = NULL 
DECLARE @sOldDateTime AS DATETIME 
DECLARE @eOldDateTime AS DATETIME 

OPEN [MyCursor] 
FETCH NEXT FROM [MyCursor] 
INTO @GUID, @No, @sDateTime, @eDateTime, @Name 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    IF (@OldName = '') 
    BEGIN 
     SET @sOldDateTime = @sDateTime 
     SET @OldName = @Name 
    END 
    ELSE IF (@OldName <> @Name OR DATEDIFF(MINUTE, @eOldDateTime, @sDateTime) > 10) 
    BEGIN 
     INSERT INTO @MyTable 
     ([GUID], [No], [sDateTime], [eDateTime], [Name]) 
     VALUES 
     (@GUID, @MergedNo, @sOldDateTime, CASE WHEN @MergedNo LIKE '%,%' THEN '**' + CONVERT(VARCHAR(100), @eOldDateTime, 20) + '**' ELSE CONVERT(VARCHAR(100), @eOldDateTime, 20) END, @Name) 

     SET @sOldDateTime = @sDateTime 
     SET @OldName = @Name 
     SET @MergedNo = NULL 
    END 

    SET @MergedNo = COALESCE(@MergedNo + ', ', '') + @No 
    SET @eOldDateTime = @eDateTime 

    FETCH NEXT FROM [MyCursor] 
    INTO @GUID, @No, @sDateTime, @eDateTime, @Name 
END 
CLOSE [MyCursor]; 
DEALLOCATE [MyCursor]; 

IF (@OldName <> '') 
BEGIN 
    INSERT INTO @MyTable 
    ([GUID], [No], [sDateTime], [eDateTime], [Name]) 
    VALUES 
    (@GUID, @MergedNo, @sOldDateTime, CASE WHEN @MergedNo LIKE '%,%' THEN '**' + CONVERT(VARCHAR(100), @eOldDateTime, 20) + '**' ELSE CONVERT(VARCHAR(100), @eOldDateTime, 20) END, @Name) 
END 

SELECT * 
FROM @MyTable 

DROP TABLE [MyTable] 
相关问题