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

时间:2016-12-26 10:41:56

标签: sql-server

我只需要通过ID在一个分区中的前100个密钥和即将到来的101密钥之间的日期时间差异,即如果下一个密钥值也为101则id = 1然后获取该密钥日期时间值。对即将到来的100键的101键也是如此。我试过窗口函数但没有得到确切的答案。

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       

3 个答案:

答案 0 :(得分:0)

这是一种方法(假设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            

答案 1 :(得分:0)

万一没有其他人来帮忙,你可以尝试一下。这很麻烦,但可以使用测试数据。

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

答案 2 :(得分:0)

        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