Sql 查询以反映数据中的实际重大变化

Sql 查询以反映数据中的实际重大变化,sql,sql-server,sql-server-2005,Sql,Sql Server,Sql Server 2005,给定一个包含员工状态和生效日期的表,如何仅检索反映状态更改的数据 例如,给定以下结构: DECLARE @STATUSES TABLE( EMPLOYEE_ID INT NOT NULL, EFFECTIVE_DATE DATE NOT NULL, STATUS_CODE CHAR(1) NOT NULL ) INSERT @STATUSES VALUES (1, '2012-01-01', 'A') INSERT @STATUSES VALUES (1, '2012-02-28',

给定一个包含员工状态和生效日期的表,如何仅检索反映状态更改的数据

例如,给定以下结构:

DECLARE @STATUSES TABLE(
  EMPLOYEE_ID INT NOT NULL,
  EFFECTIVE_DATE DATE NOT NULL,
  STATUS_CODE CHAR(1) NOT NULL
)
INSERT @STATUSES VALUES (1, '2012-01-01', 'A')
INSERT @STATUSES VALUES (1, '2012-02-28', 'A')
INSERT @STATUSES VALUES (1, '2012-03-01', 'T')
INSERT @STATUSES VALUES (2, '2012-01-01', 'A')
INSERT @STATUSES VALUES (2, '2012-02-14', 'A')
INSERT @STATUSES VALUES (2, '2012-03-10', 'A')
INSERT @STATUSES VALUES (3, '2012-02-01', 'A')
INSERT @STATUSES VALUES (3, '2012-03-17', 'A')
INSERT @STATUSES VALUES (3, '2012-03-18', 'T')
INSERT @STATUSES VALUES (3, '2012-04-01', 'A')
INSERT @STATUSES VALUES (4, '2012-03-01', 'A')
什么查询可以用于产生以下结果

EMPLOYEE_ID     EFFECTIVE_DATE      STATUS_CODE
1               2012-01-01          A
1               2012-03-01          T
2               2012-01-01          A
3               2012-02-01          A
3               2012-03-18          T
3               2012-04-01          A
4               2012-03-01          A

换言之,如果存在生效日期较早的记录,我想省去那些与之前的记录具有相同员工id和状态代码的记录。请注意,员工1只列出了两次,因为状态只有两次实际变化2012-02-28上的一次是无关紧要的,因为状态与之前的日期没有变化。还要注意,employee 2只列出了一次,因为尽管有三条记录,但他的状态从未改变。每次更改只显示最早的日期。

经过进一步的试验,看起来这会满足我的要求

SELECT
  EMPLOYEE_ID, MIN(EFFECTIVE_DATE) AS EFFECTIVE_DATE, STATUS_CODE
FROM
    (
    SELECT
      T1.EMPLOYEE_ID, T1.EFFECTIVE_DATE, T1.STATUS_CODE,
      MAX(T2.EFFECTIVE_DATE) AS MOST_RECENT_PREVIOUS_STATUS_DATE
    FROM
      @STATUSES T1
        LEFT JOIN
      @STATUSES T2
        ON
        T1.EMPLOYEE_ID = T2.EMPLOYEE_ID
          AND
        T1.EFFECTIVE_DATE > T2.EFFECTIVE_DATE
          AND
        T1.STATUS_CODE <> T2.STATUS_CODE
    GROUP BY
      T1.EMPLOYEE_ID, T1.EFFECTIVE_DATE, T1.STATUS_CODE
    ) SubQuery
GROUP BY
  EMPLOYEE_ID, STATUS_CODE, MOST_RECENT_PREVIOUS_STATUS_DATE
;WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY EMPLOYEE_ID ORDER BY EFFECTIVE_DATE) AS rownum
                ,EMPLOYEE_ID
                ,EFFECTIVE_DATE
                ,STATUS_CODE
         FROM   @STATUSES)
SELECT t2.EMPLOYEE_ID
       ,t2.EFFECTIVE_DATE
       ,t2.STATUS_CODE
FROM   cte t2
       LEFT JOIN cte t1
         ON t2.EMPLOYEE_ID = t1.EMPLOYEE_ID
            AND t2.STATUS_CODE = t1.STATUS_CODE
            AND t2.rownum = t1.rownum + 1
WHERE  t1.EMPLOYEE_ID IS NULL

通过进一步的实验,看起来这会满足我的要求

;WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY EMPLOYEE_ID ORDER BY EFFECTIVE_DATE) AS rownum
                ,EMPLOYEE_ID
                ,EFFECTIVE_DATE
                ,STATUS_CODE
         FROM   @STATUSES)
SELECT t2.EMPLOYEE_ID
       ,t2.EFFECTIVE_DATE
       ,t2.STATUS_CODE
FROM   cte t2
       LEFT JOIN cte t1
         ON t2.EMPLOYEE_ID = t1.EMPLOYEE_ID
            AND t2.STATUS_CODE = t1.STATUS_CODE
            AND t2.rownum = t1.rownum + 1
WHERE  t1.EMPLOYEE_ID IS NULL
你可以用光标

您需要两组变量:@PreviousRecord和@CurrentRecord

为按employeeid和日期排序的表声明光标

将光标中的第一条记录提取到@PreviousRecord变量中-根据您的要求将其注册为重大更改或不将记录写入临时表

然后设置一个循环: 将下一条记录提取到@CurrentRecord变量中 将其与以前的记录进行比较,如果它符合您对重大更改的要求,则将其写入临时表 将@CurrentRecord值移动到@PreviousRecord变量中

我想知道CTE方法是否更有效

您可以使用光标

您需要两组变量:@PreviousRecord和@CurrentRecord

为按employeeid和日期排序的表声明光标

将光标中的第一条记录提取到@PreviousRecord变量中-根据您的要求将其注册为重大更改或不将记录写入临时表

然后设置一个循环: 将下一条记录提取到@CurrentRecord变量中 将其与以前的记录进行比较,如果它符合您对重大更改的要求,则将其写入临时表 将@CurrentRecord值移动到@PreviousRecord变量中


我很想知道CTE方法是否更有效

或者,您可以在ON子句中添加和t2.STATUS_CODE=t1.STATUS_CODE,并将WHERE子句更改为类似于WHERE t1.EMPLOYEE_ID为NULL的形式。@AndriyM说得好,我喜欢读起来更好的方式,谢谢。更新。或者,您可以将AND t2.STATUS\U CODE=t1.STATUS\U CODE添加到ON子句中,并将WHERE子句更改为类似WHERE t1.EMPLOYEE\U ID为NULL的内容。@AndriyM说得好,我喜欢这样读起来更好,谢谢。更新。