【问题标题】:Aggregate on Datetime Column for Pivot聚合数据透视的日期时间列
【发布时间】:2013-03-07 06:26:58
【问题描述】:

我有一个存储员工登录信息的表

登录表的示例数据是

 EmpID  Status     [Time]
 100    SignIn    2013-03-07 11:41:44.473
 101    SignIn    2013-03-07 10:41:44.473
 100    SignOut   2013-03-07 12:41:44.473
 101    SignOut   2013-03-07 11:41:44.473
 101    SignIn    2013-03-08 11:41:44.473

我希望结果是

EmpID   SignIn                    SignOut
 100    2013-03-07 11:41:44.473    2013-03-07 12:41:44.473
 101    2013-03-07 10:41:44.473    2013-03-07 11:41:44.473
 101    2013-03-08 11:41:44.473    NULL

我尝试使用 PIVOT

Select EmpID,[SignIn],[SignOut]
from 
(Select EmpId,status,LoginTime from Login)p
 pivot
(
 min(Logintime)
 For status in ([SignIn],[SignOut])
)pvt

但是上面的查询遗漏了Employee 101 的最后一行,它具有SignIn 时间但no SignOut

SQLFiddle用于生成表数据

【问题讨论】:

    标签: sql sql-server sql-server-2008-r2 pivot


    【解决方案1】:

    您使用PIVOT 函数走在正确的轨道上,您只需要一种基于empidstatusdate/ 为每一行分配row_number() 的方法您可以使用以下pivot代码:

    Select EmpID,[SignIn],[SignOut]
    from 
    (
      Select EmpId, status, 
        LoginTime, 
        cast(logintime as date) date,
        row_number() over(partition by empid, status, cast(logintime as date)
                          order by logintime) rn
      from Login
    )p
    pivot
    (
      min(Logintime)
      For status in ([SignIn],[SignOut])
    )pvt
    

    SQL Fiddle with Demo

    您会注意到子查询中有两个新列。一个为每一行生成一个row_number(),第二个在没有时间的情况下生成date。这两个列都在GROUP BY 中使用,但未显示在最终选择中。它们被使用,因此您每天可以为每个员工获取多行(如果需要)。

    使用来自 sql fiddle 的@IvanG 的数据的结果是:

    | EMPID |              SIGNIN |             SIGNOUT |
    -----------------------------------------------------
    |   100 | 2013-03-07 11:41:44 | 2013-03-07 12:41:44 |
    |   101 | 2013-03-07 10:41:44 | 2013-03-07 11:41:44 |
    |   101 | 2013-03-08 11:41:44 |              (null) |
    |   102 | 2013-04-08 12:41:44 | 2013-04-08 13:41:44 |
    |   102 | 2013-04-08 16:41:44 | 2013-04-08 17:41:44 |
    |   102 | 2013-04-08 19:41:44 |              (null) |
    

    【讨论】:

    • @praveen 我忍不住,我喜欢这些问题! :)
    • 当我看到这个问题是关于 pivot 并且没有解决时,我想“bluefeet 可能不在线”:)
    • @IvanG 人们只是期待我的回答,这有点令人遗憾! :D
    • @bluefeet 我猜他们没办法,因为你太擅长了:D
    【解决方案2】:

    你可以这样得到它:

    SELECT  l1.EmpID
            , l1.LoginTime [SignIn]
            , l2.LoginTime [SignOut]
    FROM    Login l1
    LEFT JOIN   
            Login l2 ON 
            l2.EmpID = l1.EmpID
    AND     CAST(l2.LoginTime AS DATE) = CAST(l1.LoginTime AS DATE)
    AND     l2.status = 'SignOut'
    WHERE   l1.status = 'SignIn'
    

    请注意,如果您每天有多个员工登录/退出,并且您想在一天内获得他的第一个 SignIn 和最后一个 SignOut,则必须更改查询:

    SELECT  l1.EmpID
            , MIN(l1.LoginTime) [SignIn]
            , MAX(l2.LoginTime) [SignOut]
    FROM    Login l1
    LEFT JOIN   
            Login l2 ON 
            l2.EmpID = l1.EmpID
    AND     CAST(l2.LoginTime AS DATE) = CAST(l1.LoginTime AS DATE)
    AND     l2.status = 'SignOut'
    WHERE   l1.status = 'SignIn'
    GROUP BY
            l1.EmpID, CAST(l1.LoginTime AS DATE)
    

    这是另一个查询,它也适用于同一天用户的多次登录/注销。这将列出他一天内的所有登录/注销:

    ;WITH cte1 AS
    (
        SELECT  *
                , ROW_NUMBER() OVER 
                    (PARTITION BY EmpID, CAST(LoginTime AS DATE) ORDER BY LoginTime) 
                    AS num
        FROM    Login
    )
    
    SELECT  l1.EmpID
            , l1.LoginTime [SignIn]
            , l2.LoginTime [SignOut]
    FROM    cte1 l1
    LEFT JOIN   
            cte1 l2 ON 
            l2.EmpID = l1.EmpID
    AND     CAST(l2.LoginTime AS DATE) = CAST(l1.LoginTime AS DATE)
    AND     l2.num = l1.num + 1
    WHERE   l1.status = 'SignIn'
    

    这里是 SQL Fiddle 用于在一天内处理用户的多个登录/注销场景的最后两个查询,为此我添加了带有EmpID 102 的用户来采样数据。

    【讨论】:

    • 感谢您提供不同的解决方案!
    • 不客气,我认为介绍一些其他情况可能会很好,所以我添加了第二个和第三个查询。
    猜你喜欢
    • 1970-01-01
    • 2015-01-03
    • 1970-01-01
    • 1970-01-01
    • 2018-01-14
    • 2022-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多