如果字段与其他行中的其他字段之间的不同时间几乎相同,我如何对行进行分组?
例如:
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
如果不同的行不超过10分钟,如何从其他行的sDateTime
行获取eDateTime
?
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 :(得分:3)
这里没有游标,但这仍然是一个循环,使用递归CTE来确定哪些行应该组合在一起。临时表#T
用于保存将行相互连接的ID
。它可以在没有临时表的情况下完成,但出于性能原因,最好使用临时表而不是使用另一个CTE作为递归CTE的源。
在最终查询中,我使用for xml path
技巧来连接No
中的值。这意味着第二次使用CTE C
,因此它将被执行两次。您可能还想将该CTE转换为临时表以避免这种情况。
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
答案 1 :(得分: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]