SQL - 在列值更改之前和之后选择包含日期的行

时间:2016-01-08 10:55:10

标签: sql sql-server

我有一张名为test的表  在测试中,我有一个ID,一个值和一个日期 每个ID都会订购日期  我想在更改值之前和之后为ID选择行,因此以下示例表。

RowNum -------- ID -------值--------日期
1 ------------------ 001 --------- 1 ----------- 01/01/2015
2 ------------------ 001 --------- 1 ----------- 02/01/2015
3 ------------------ 001 --------- 1 ----------- 04/01/2015
4 ------------------ 001 --------- 1 ----------- 05/01/2015
5 ------------------ 001 --------- 1 ----------- 06/01/2015
6 ------------------ 001 --------- 1 ----------- 08/01/2015
7 ------------------ 001 --------- 0 ----------- 09/01/2015
8 ------------------ 001 --------- 0 ----------- 10/01/2015
9 ------------------ 001 --------- 0 ----------- 11/01/2015
10 ----------------- 001 --------- 1 ----------- 12/01/2015
11 ----------------- 001 --------- 1 ----------- 14/01/2015
12 ------------------ 002 --------- 1 ----------- 01/01/2015
13 ------------------ 002 --------- 1 ----------- 04/01/2015
14 ------------------ 002 --------- 0 ----------- 05/01/2015
15 ------------------ 002 --------- 0 ----------- 07/01/2015

结果将返回第6,7,9,10,13,14行

3 个答案:

答案 0 :(得分:4)

您可以使用分析函数LAG()LEAD()来访问前后行中的值,然后检查它是否与当前行中的值不匹配。

SELECT *
FROM   (
  SELECT RowNum,
         ID,
         Value,
         Date,
         LAG(VALUE, 1, VALUE)  OVER(ORDER BY RowNum)  PrevValue,
         LEAD(VALUE, 1, VALUE) OVER(ORDER BY RowNum)  NextValue
  FROM   test)
WHERE PrevValue <> Value
OR    NextValue <> Value

传递给这个函数的参数是

  1. 一些标量表达式(本例中为列名);
  2. 偏移(前后1行);
  3. 默认值(LAG()将返回第一行NULL,而LEAD()将返回NULL作为最后一行,但它们在您的问题中看起来并不特别,所以我使用列值作为默认值。

答案 1 :(得分:1)

请参阅以下内容,不使用LEAD和LAG:

DECLARE @i        INT = 1, 
        @cnt      INT, 
        @dstvalue INT, 
        @srcvalue INT 

CREATE TABLE #result 
  ( 
     id     INT, 
     mydate DATE 
  ) 

CREATE TABLE #temp1 
  ( 
     rn     INT IDENTITY(1, 1), 
     id     INT, 
     mydate DATE 
  ) 

INSERT INTO #temp1 
            (id, 
             mydate) 
SELECT id, 
       mydate 
FROM   table 
ORDER  BY id, 
          mydate 

SELECT @cnt = Count(*) 
FROM   #temp1 

SELECT @srcvalue = value 
FROM   #temp1 
WHERE  rn = @i 

WHILE ( @i <= @cnt ) 
  BEGIN 
      SELECT @dstvalue = value 
      FROM   #temp1 
      WHERE  rn = @i 

      IF( @srcvalue = @dstvalue ) 
        BEGIN 
            SET @i = @i + 1 

            CONTINUE; 
        END 
      ELSE 
        BEGIN 
            SET @srcvalue = @dstvalue 

            INSERT INTO #result 
                        (id, 
                         mydate) 
            SELECT id, 
                   mydate 
            FROM   #temp 
            WHERE  rn = @i - 1 
            UNION ALL 
            SELECT id, 
                   mydate 
            FROM   #temp 
            WHERE  rn = @i 
        END 

      SET @i = @i + 1 
  END 

SELECT * 
FROM   #result 

答案 2 :(得分:1)

使用lag()lead()的答案是正确答案。如果您使用的是SQL Server 2012之前版本,那么您可以使用cross apply或相关子查询执行相同的操作:

select t.*
from test t cross apply
     (select top 1 tprev.*
      from test tprev
      where tprev.date < t.date
      order by date desc
     ) tprev cross apply
     (select top 1 tnext.*
      from test tnext
      where tnext.date > t.date
      order by date asc
     ) tnext 
where tprev.value <> tnext.value;