比较SQL Server中的第一行和第n行

问题描述:

我只需要按ID在一个分区中的前100个密钥和即将到来的101个密钥之间的日期时间差异,即如果下一个密钥值也为101,则采用该关键日期时间值,即id = 1。 101键即将到来的100键也一样。我尝试使用窗口函数,但没有得到确切的答案。比较SQL Server中的第一行和第n行

ID Name Key  datetime 
1 AAA  100  2016-07-01 09:32:48.000 
1 AAA  100  2016-07-01 13:31:02.000 
1 AAA  100  2016-07-01 14:10:57.000 
1 AAA  101  2016-07-01 15:12:09.000 
1 AAA  100  2016-07-01 15:12:17.000 
1 AAA  100  2016-07-01 15:18:36.000 
1 AAA  101  2016-07-01 15:34:16.000 
2 BBB  100  2016-07-04 09:26:15.000 
2 BBB  100  2016-07-04 13:40:52.000 
2 BBB  101  2016-07-04 14:18:26.000 
2 BBB  101  2016-07-04 18:34:29.000 
2 BBB  100  2016-07-04 18:34:32.000 
2 BBB  101  2016-07-04 23:04:32.000 

进出料应该像

ID Name Key  datetime     In   Out 
1 AAA  100  2016-07-01 09:32:48.000 05:39:21  
1 AAA  100  2016-07-01 13:31:02.000  
1 AAA  100  2016-07-01 14:10:57.000  
1 AAA  101  2016-07-01 15:12:09.000    00:06:27 
1 AAA  100  2016-07-01 15:12:17.000 00:21:59  
1 AAA  100  2016-07-01 15:18:36.000  
1 AAA  101  2016-07-01 15:34:16.000  
2 BBB  100  2016-07-04 09:26:15.000 09:08:14  
2 BBB  100  2016-07-04 13:40:52.000  
2 BBB  101  2016-07-04 14:18:26.000  
2 BBB  101  2016-07-04 18:34:29.000    00:00:03 
2 BBB  100  2016-07-04 18:34:32.000 04:30:00  
2 BBB  101  2016-07-04 23:04:32.000  
+2

添加预期的输出 –

+0

古温德·辛格datetime列值之差即差 '2016年7月1日09:32:48.000' 和“2016年7月1日15 :12:09.000' – Jalandhar

+1

请编辑您的问题并解释输出 – TheGameiswar

下面是做这件事(假设SQL Server 2012中或更高版本)

首先,创建和填充示例表(,请将您的未来问题省略我们这一步)

DECLARE @T AS TABLE 
(
    Id int, 
    Name char(3), 
    [Key] int, 
    [datetime] DateTime 
) 

INSERT INTO @T VALUES 
(1, 'AAA', 100, '2016-07-01 09:32:48.000'), 
(1, 'AAA', 100, '2016-07-01 13:31:02.000'), 
(1, 'AAA', 100, '2016-07-01 14:10:57.000'), 
(1, 'AAA', 101, '2016-07-01 15:12:09.000'), 
(1, 'AAA', 100, '2016-07-01 15:12:17.000'), 
(1, 'AAA', 100, '2016-07-01 15:18:36.000'), 
(1, 'AAA', 101, '2016-07-01 15:34:16.000'), 
(2, 'BBB', 100, '2016-07-04 09:26:15.000'), 
(2, 'BBB', 100, '2016-07-04 13:40:52.000'), 
(2, 'BBB', 101, '2016-07-04 14:18:26.000'), 
(2, 'BBB', 101, '2016-07-04 18:34:29.000'), 
(2, 'BBB', 100, '2016-07-04 18:34:32.000'), 
(2, 'BBB', 101, '2016-07-04 23:04:32.000') 

查询:

SELECT Id, 
     Name, 
     [Key], 
     [DateTime], 
     CASE WHEN [Key] = 100 AND ISNULL(LAG([Key]) OVER(PARTITION BY Id ORDER BY [DateTime]), 101) <> 100 THEN 
      CAST(
       (SELECT TOP 1 [DateTime] 
       FROM @T subQuery 
       WHERE subQuery.[Key] = 101 
       AND subQuery.Id = outerQuery.Id 
       AND subQuery.[DateTime] > outerQuery.[DateTime] 
       ORDER BY [DateTime] 
       ) - [DateTime] 
      As Time) 
     ELSE 
      NULL 
     END As [In], 
     CASE WHEN [Key] = 101 AND ISNULL(LAG([Key]) OVER(PARTITION BY Id ORDER BY [DateTime]), 100) <> 101 THEN 
      CAST(
       (SELECT TOP 1 [DateTime] 
       FROM @T subQuery 
       WHERE subQuery.[Key] = 100 
       AND subQuery.Id = outerQuery.Id 
       AND subQuery.[DateTime] > outerQuery.[DateTime] 
       ORDER BY [DateTime] 
       ) - [DateTime] 
      As Time) 
     ELSE 
      NULL 
     END As [Out] 
FROM @T outerQuery 

结果:

Id Name Key  DateTime    In   Out 
1 AAA  100  01.07.2016 09:32:48  05:39:21  
1 AAA  100  01.07.2016 13:31:02    
1 AAA  100  01.07.2016 14:10:57    
1 AAA  101  01.07.2016 15:12:09     00:00:08 
1 AAA  100  01.07.2016 15:12:17  00:21:59  
1 AAA  100  01.07.2016 15:18:36    
1 AAA  101  01.07.2016 15:34:16    
2 BBB  100  04.07.2016 09:26:15  04:52:11  
2 BBB  100  04.07.2016 13:40:52    
2 BBB  101  04.07.2016 14:18:26     04:16:06 
2 BBB  101  04.07.2016 18:34:29    
2 BBB  100  04.07.2016 18:34:32  04:30:00  
2 BBB  101  04.07.2016 23:04:32    
+0

你快到了。我正在研究类似的答案。从这个问题:'如果下一个键值也是101,那么取这个关键日期时间值'这意味着你的结果第10行与out ='04:16:06'是不同于他的预期输出。 – SqlZim

+0

嗯,我想我有点误解了那部分...不要以为我会有时间来修改我今天的答案,也许明天。 –

+0

谢谢你Zohar Peled你解决了我的问题。有性能问题,但得到了解决方案,谢谢。 – Jalandhar

万一没人走来帮忙,你可以试试这个。这是一团糟,但与测试数据一起工作。

rextester:http://rextester.com/MPD86507

create table KeySwipeLog (Id int, Name char(3),KeyCard int, dt datetime); 
insert into KeySwipeLog values 
    (1,'aaa',100,'2016-07-01 09:32:48.000') 
    ,(1,'aaa',100,'2016-07-01 13:31:02.000') 
    ,(1,'aaa',100,'2016-07-01 14:10:57.000') 
    ,(1,'aaa',101,'2016-07-01 15:12:09.000') 
    ,(1,'aaa',100,'2016-07-01 15:12:17.000') 
    ,(1,'aaa',100,'2016-07-01 15:18:36.000') 
    ,(1,'aaa',101,'2016-07-01 15:34:16.000') 
    ,(2,'bbb',100,'2016-07-04 09:26:15.000') 
    ,(2,'bbb',100,'2016-07-04 13:40:52.000') 
    ,(2,'bbb',101,'2016-07-04 14:18:26.000') 
    ,(2,'bbb',101,'2016-07-04 18:34:29.000') 
    ,(2,'bbb',100,'2016-07-04 18:34:32.000') 
    ,(2,'bbb',101,'2016-07-04 23:04:32.000'); 


with cte as (
    select 
     Id 
    , Name 
    , Keycard 
    , Dt 
    , ignore = case 
     when KeyCard = 100 
      and isnull(lag(k.KeyCard) over (partition by k.Id order by k.dt),0)!=100 
      then 0 
     when KeyCard = 101 and isnull(lead(k.KeyCard) over (partition by k.Id order by k.dt),0)!=101 
      then 0 
     else 1 
     end 
     /* When the next Keycard is also 101, skip the current record for calculating in times*/ 
     , in_ignore = case 
     when KeyCard = 101 
      and isnull(lead(k.KeyCard) over (partition by k.Id order by k.dt),0)!=101 
      then 0 
     --when KeyCard = 100 then null 
     else 1 
     end 
     /* When the next Keycard is also 100, skip the current record for calculating out times*/ 
     , out_ignore = case 
     when KeyCard = 100 
      and isnull(lead(k.KeyCard) over (partition by k.Id order by k.dt),0) = 100 
      then 1 
     --when KeyCard = 101 then 1 
     else 0 
     end 
    from KeySwipeLog as k 
    ) 

select 
    k.Id 
    , k.Name 
    , k.KeyCard 
    , k.dt 
    --, k.ignore 
    --, k.in_ignore 
    --, k.out_ignore 
, [in] =case 
    when k.KeyCard=100 
    and i.dt is not null 
    and k.ignore = 0 
    then convert(varchar(8),convert(time(0),i.dt-k.dt)) 
    else '' 
    end 
, out=case 
    when k.KeyCard=101 
    and o.dt is not null 
    and k.ignore = 0 
    then convert(varchar(8),convert(time(0),o.dt-k.dt)) 
    else '' 
    end 
--, i.* 
--, o.* 
    from cte as k 
     outer apply (
      select top 1 * 
      from cte as x 
      where x.Id=k.Id 
      and x.dt>k.dt 
      and x.keycard!=k.keycard 
      and x.in_ignore=0 
      order by dt asc 
     ) as i 
     outer apply (
      select top 1 * 
      from cte as x 
      where x.Id=k.Id 
      and x.dt>k.dt 
      and x.keycard!=k.keycard 
      and x.out_ignore=0 
      order by dt asc 
     ) as o 

    order by k.Id, k.dt 

 DECLARE @T AS TABLE 
     (
     Id int, 
     Name char(3), 
     [Key] int, 
     [datetime] DateTime 
     ) 

     INSERT INTO @T VALUES 
     (1, 'AAA', 100, '2016-07-01 09:32:48.000'), 
     (1, 'AAA', 100, '2016-07-01 13:31:02.000'), 
     (1, 'AAA', 100, '2016-07-01 14:10:57.000'), 
     (1, 'AAA', 101, '2016-07-01 15:12:09.000'), 
     (1, 'AAA', 100, '2016-07-01 15:12:17.000'), 
     (1, 'AAA', 100, '2016-07-01 15:18:36.000'), 
     (1, 'AAA', 101, '2016-07-01 15:34:16.000'), 
     (2, 'BBB', 100, '2016-07-04 09:26:15.000'), 
     (2, 'BBB', 100, '2016-07-04 13:40:52.000'), 
     (2, 'BBB', 101, '2016-07-04 14:18:26.000'), 
     (2, 'BBB', 101, '2016-07-04 18:34:29.000'), 
     (2, 'BBB', 100, '2016-07-04 18:34:32.000'), 
     (2, 'BBB', 101, '2016-07-04 23:04:32.000') 


     ;with cte as 
     (

     SELECT *,ROW_NUMBER() OVER(PARTITION BY ID,NAMe ORDER BY [datetime])RN, 
      CASE WHEN LAG([Key],1) OVER(PARTITION BY ID,NAMe ORDER BY [datetime]) = [KEY] THEN 0 ELSE 1 END IsDiffer 
     FROM @t 
     ) 

     SELECT c1.*, 
       CASE WHEN c1.[Key] = 100 THEN 
        CAST(DATEDIFF(ss,c1.[datetime],E.[datetime]) /3600 AS VARCHAR) +':'+ 
       CAST((DATEDIFF(ss,c1.[datetime],E.[datetime]) %3600)/60 AS VARCHAR) +':'+ 
       CAST((DATEDIFF(ss,c1.[datetime],E.[datetime]) %60) AS VARCHAR) END [IN], 
       CASE WHEN c1.[Key] = 101 THEN 
        CAST(DATEDIFF(ss,c1.[datetime],E.[datetime]) /3600 AS VARCHAR) +':'+ 
       CAST((DATEDIFF(ss,c1.[datetime],E.[datetime]) %3600)/60 AS VARCHAR) +':'+ 
       CAST((DATEDIFF(ss,c1.[datetime],E.[datetime]) %60) AS VARCHAR) END [OUT] 

     FROM CTE c1 
     OUTER APPLY 
     (
     SELECT TOP 1 * FROM cte c2 
     where c1.id = c2.id and c1.name = c2.name 
     and c2.IsDiffer = 1 and c1.IsDiffer = 1 
     and c1.rn <c2.rn 
     ORDER BY RN 
     )E