当前一行的列值发生更改时选择行

时间:2017-07-20 08:31:56

标签: sql sql-server stored-procedures sql-server-2014

我有下表: -

Name   Status  Timestamp
Ben      1     2015-01-01
Ben      1     2015-01-02
Joe      1     2015-11-12   
Joe      2     2015-11-13
Joe      2     2016-12-14
Joe      2     2016-12-15
Paul     1     2015-08-16
Paul     1     2015-08-17
Paul     3     2015-08-18
Paul     3     2015-08-19
Mark     2     2015-09-20
Mark     2     2015-09-25
Mark     2     2015-09-26
Mark     3     2015-10-27

我需要一个只返回“状态”变化的行的查询。它应该在'状态'已更改,也是前一行。 例如,结果应如下所示: -

Name   Status  Timestamp
Joe      1     2015-11-12   
Joe      2     2015-11-13
Paul     1     2015-08-17
Paul     3     2015-08-18
Mark     2     2015-09-26
Mark     3     2015-10-27

如何实现这一结果。

2 个答案:

答案 0 :(得分:2)

您可以使用CTECASE以及LAGLEAD来计算要选择的行。这适用于2012及更高版本:

创建并填充样本表(在将来的问题中保存此步骤)

DECLARE @T as TABLE
(
    Name varchar(4),
    [Status] int,
    [Timestamp] date
)

INSERT INTO @T VALUES
('Joe', 1, '2015-11-12'),   
('Joe', 2, '2015-11-13'),
('Joe', 2, '2016-12-14'),
('Joe', 2, '2016-12-15'),
('Paul' ,1, '2015-08-16'),
('Paul' ,1, '2015-08-17'),
('Paul' ,3, '2015-08-18'),
('Paul' ,3, '2015-08-19'),
('Mark' ,2, '2015-09-20'),
('Mark' ,2, '2015-09-25'),
('Mark' ,2, '2015-09-26'),
('Mark' ,3, '2015-10-27')

cte - 请注意,我在case表达式中使用了lag和lead。

;WITH CTE AS
(
    SELECT  Name,
            [Status],
            [Timestamp],
            CASE WHEN LAG([Status]) OVER(PARTITION BY Name ORDER BY [Timestamp]) <> [Status] OR
                      LEAD([Status]) OVER(PARTITION BY Name ORDER BY [Timestamp]) <> [Status] THEN 
                1
            END As Filter
    FROM @T
)

查询:

SELECT  Name,
        [Status],
        [Timestamp]
FROM CTE
WHERE Filter = 1

结果:

Name    Status  Timestamp
Joe     1       12.11.2015 00:00:00
Joe     2       13.11.2015 00:00:00
Mark    2       26.09.2015 00:00:00
Mark    3       27.10.2015 00:00:00
Paul    1       17.08.2015 00:00:00
Paul    3       18.08.2015 00:00:00

See a live demo on rextester

答案 1 :(得分:0)

这可以是从2005年开始的用户:

declare @t table (Name varchar(100),   S int, T date);
insert into @t values
('Joe',     1     ,'2015-11-12'),   
('Joe',     2     ,'2015-11-13'),
('Joe',     2     ,'2016-12-14'),
('Joe',     2     ,'2016-12-15'),
('Paul',    1     ,'2015-08-16'),
('Paul',    1     ,'2015-08-17'),
('Paul',    3     ,'2015-08-18'),
('Paul',    3     ,'2015-08-19'),
('Mark',     2     ,'2015-09-20'),
('Mark',     2     ,'2015-09-25'),
('Mark',     2     ,'2015-09-26'),
('Mark',     3     ,'2015-10-27');

with cte as 
(
select *, ROW_NUMBER() over(partition by Name order by T) as rn
from @t
)

,cte1 as
(
select c1.Name, c1.S as S1, c1.T as T1, c2.S as S2, c2.T as T2
from cte c1 join cte c2
       on c1.rn + 1 = c2.rn
          and c1.Name = c2.Name
where c1.S <> c2.S
)

select Name,
       case n 
              when 1 then S1
              when 2 then S2
       end as Status,
       case n 
              when 1 then T1
              when 2 then T2
       end as Timestamp
from cte1 cross join (select 1 n union all select 2) nums;

这是相同的,但对于从2012年开始的版本:

declare @t table (Name varchar(100),   S int, T date);
insert into @t values
('Joe',     1     ,'2015-11-12'),   
('Joe',     2     ,'2015-11-13'),
('Joe',     2     ,'2016-12-14'),
('Joe',     2     ,'2016-12-15'),
('Paul',    1     ,'2015-08-16'),
('Paul',    1     ,'2015-08-17'),
('Paul',    3     ,'2015-08-18'),
('Paul',    3     ,'2015-08-19'),
('Mark',     2     ,'2015-09-20'),
('Mark',     2     ,'2015-09-25'),
('Mark',     2     ,'2015-09-26'),
('Mark',     3     ,'2015-10-27');

with cte as 
(
select name,
       S as S1, 
       lead(S) over(partition by Name order by T) S2,
       T as T1, 
       lead(T) over(partition by Name order by T) T2
from @t
)

,cte1 as
(
select *
from cte
where S1 <> S2
)

select Name,
       case n 
              when 1 then S1
              when 2 then S2
       end as Status,
       case n 
              when 1 then T1
              when 2 then T2
       end as Timestamp

from cte1 cross join (select 1 n union all select 2) nums;
相关问题