使用SQL语句将日志数据汇总到更改历史记录中

时间:2013-08-19 15:43:26

标签: sql sql-server sql-server-2008

我有一张表,其中包含员工的时间戳记行动记录。每条记录都有一个部门和一个工作头衔。现在,我想提取员工更改部门和/或工作头衔时发生的更改。

我正在使用SQL Server 2008。

假设我们的表保存了一个简单员工的记录,数据可能如下所示:

Time |  Department | WorkTitle
t1        Dep1         Wt1    <---
t2        Dep1         Wt1
t3        Dep2         Wt2    <---
t4        Dep2         Wt2
t5        Dep1         Wt1    <---
t6        Dep3         Wt1    <---
t7        Dep3         Wt1
t8        Dep3         Wt1

我希望在员工出现在新部门和/或拥有新工作头衔时提取第一个实例。

在上述数据中,带箭头的记录应该是提取的记录,并且应该得到以下结果:

Time |  Department | WorkTitle
t1        Dep1         Wt1
t3        Dep2         Wt2
t5        Dep1         Wt1
t6        Dep3         Wt1

请注意,在时间t1和t5出现相同的部门和工作标题,因此简单的GROUP BY子句不起作用。

我尝试了一些使用OVER / PARTITION的尝试,但这个查询的复杂性似乎超出了我的想法。

可以使用SQL语句完成吗?

3 个答案:

答案 0 :(得分:3)

您真正需要的功能是lag(),但直到SQL Server 2012才可用。在缺少的情况下,我更喜欢相关的子查询。

此方法检索上一次,然后将表重新加入表中,并进行比较以进行过滤:

select tprev.*
from (select t.*,
             (select top 1 time
              from t t2
              where t.time < t2.time
              order by time desc
             ) as prevtime
      from t
     ) tprev join
     t
     on tprev.prevtime = t.time
where tprev.department <> t.department or
      tprev.worktitle <> t.worktitle or
      tprev.prevtime is null

答案 1 :(得分:2)

正如Gordon Linoff所说,当你有lag()功能时,这个问题非常容易。 SQL Server 2008没有它,因此我更倾向于使用outer apply解决它:

select t1.*
from t as t1
    outer apply (
        select top 1 t2.*
        from t as t2
        where t2.worktime < t1.worktime
        order by t2.worktime desc
    ) as t2
where
    t2.worktime is null or
    t2.department <> t1.department or
    t2.worktitle <> t1.worktitle

答案 2 :(得分:0)

您可以尝试这种非triangular join解决方案:

DECLARE @MyTable TABLE(
    -- You should use appropiate data types for every column
    [Time]      VARCHAR(20) NOT NULL,
    Department  VARCHAR(20) NOT NULL,
    WorkTitle   VARCHAR(20) NOT NULL
);

INSERT INTO @MyTable ([Time],Department,WorkTitle)
SELECT 't1', 'Dep1', 'Wt1'
UNION ALL SELECT 't2', 'Dep1', 'Wt1'
UNION ALL SELECT 't3', 'Dep2', 'Wt2'
UNION ALL SELECT 't4', 'Dep2', 'Wt2'
UNION ALL SELECT 't5', 'Dep1', 'Wt1'
UNION ALL SELECT 't6', 'Dep3', 'Wt1'
UNION ALL SELECT 't7', 'Dep3', 'Wt1'
UNION ALL SELECT 't8', 'Dep3', 'Wt1';

DECLARE @ResultsWithRowNum TABLE
(
    RowNum      INT PRIMARY KEY,
    [Time]      VARCHAR(20) NOT NULL,
    Department  VARCHAR(20) NOT NULL,
    WorkTitle   VARCHAR(20) NOT NULL
);
INSERT  @ResultsWithRowNum(RowNum,[Time],Department,WorkTitle)
SELECT  ROW_NUMBER() OVER(ORDER BY x.[Time]) AS RowNum,x.[Time],x.Department,x.WorkTitle
FROM    @MyTable x;

WITH RecursiveCTE
AS
(
    SELECT  crt.RowNum,
            crt.[Time],
            crt.Department,
            crt.WorkTitle,
            1 AS IsFirstRowNewGroup
    FROM    @ResultsWithRowNum crt
    WHERE   crt.RowNum=1
    UNION ALL
    SELECT  crt.RowNum,
            crt.[Time],
            crt.Department,
            crt.WorkTitle,
            CASE WHEN prev.Department = crt.Department AND prev.WorkTitle = crt.WorkTitle THEN 0 ELSE 1 END
    FROM    @ResultsWithRowNum crt
    INNER JOIN RecursiveCTE prev ON crt.RowNum = prev.RowNum + 1
)
SELECT  *
FROM    RecursiveCTE rec
WHERE   rec.IsFirstRowNewGroup = 1
OPTION (MAXRECURSION 0);

结果:

RowNum Time Department WorkTitle IsFirstRowNewGroup
------ ---- ---------- --------- ------------------
1      t1   Dep1       Wt1       1
3      t3   Dep2       Wt2       1
5      t5   Dep1       Wt1       1
6      t6   Dep3       Wt1       1