2013-02-12 14 views
6

Làm cách nào để nhóm các hàng nếu thời gian khác nhau giữa trường và trường khác trong hàng khác gần như giống nhau?Làm cách nào để nhóm các hàng theo DateTime nếu các giá trị gần như giống nhau?

Ví dụ:

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 

Làm thế nào tôi có thể nhận được sDateTime từ hàng với eDateTime từ hàng khác nếu khác nhau không quá 10 phút?

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

Bạn đang sử dụng phiên bản SQL Server nào? –

+0

@NitinMidha SQL Server 2008 Express Edition –

+0

Ngoài ra tại sao 05 không được kết hợp với 01,02 và 04? Tôi nghĩ rằng nó đáp ứng các tiêu chí của 10 phút, nếu tôi hiểu chính xác .. –

Trả lời

3

Không con trỏ ở đây nhưng đây vẫn là một vòng lặp qua các hàng bằng cách sử dụng CTE đệ quy để tìm ra những hàng nào sẽ được nhóm lại với nhau. Bảng tạm thời #T là để giữ ID kết nối các hàng với nhau. Nó có thể đã được thực hiện mà không có bảng tạm thời nhưng vì lý do hiệu suất nó là tốt hơn để làm điều đó với một bảng tạm thời thay vì có một CTE như là nguồn cho CTE đệ quy.

Trong truy vấn cuối cùng, tôi sử dụng thủ thuật for xml path để ghép các giá trị từ No. Điều đó có nghĩa là CTE C được sử dụng lần thứ hai để nó sẽ được thực thi hai lần. Bạn có thể muốn biến CTE đó thành một bảng tạm thời để tránh điều đó.

SQL Fiddle

MS SQL Server 2008 Schema Cài đặt:

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'); 

Query 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 | 

Chỉ để cho vui, phiên bản 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

Cảm ơn bạn rất nhiều –

1

tôi đã không thể thực hiện điều này mà không một con trỏ, có lẽ ai đó thông minh hơn có thể đưa ra một giải pháp mà không bao gồm con trỏ, nhưng đây là những gì tôi có:

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]