【问题标题】:TSQL Converting rows into columns OR Grouping rowsTSQL将行转换为列或分组行
【发布时间】:2013-07-02 20:29:45
【问题描述】:

我必须计算每个员工的轮班时间。

我编写了一个查询,从我们的 OLTP 系统获取数据并返回日期、员工和他们的登录注销时间。登录和注销时间是系统中的两个独立事务,但我希望登录和注销时间在一行中。请参阅下面的图像,我得到什么作为输入以及我想要什么作为输出。请帮助写下一个查询以实现我的目标。

【问题讨论】:

  • 我写了一个查询来获取数据也许你应该考虑发布你尝试过的查询。

标签: sql sql-server tsql subquery sql-server-2012


【解决方案1】:

您真正想要的是lag() 函数,但在 SQL Server 2008 中不可用。因此,您可以使用子查询来模拟它:

      select i.day, i.staff, i.login,
             (select top 1 logout
              from input i2
              where i2.staff = i.staff and
                    i2.day = i.day and
                    i2.login = cast(login as date) and
                    i2.logout >= i.login
              order by i2.logout
             ) as LogOut
      from input i
      where logout = cast(logout as date)

逻辑logout = cast(logout as date) 旨在识别logout 的时间分量为零的行。

这假设没有一个班次超过午夜。

编辑:

在 SQL Server 2012 中,您可以使用相同的查询。用lead() 替换它有点麻烦:

      select day, staff, login, logout
      from (select i.day, i.staff, thedate as login, which,
                   lead(thedate) over (partition by staff, day order by date) as LogOut
            from (select i.day, i.staff,
                         (case when login = cast(login as date) then logout
                               else login
                          end) as thedate,
                         (case when login = cast(login as date) then 'logout'
                               else 'login'
                          end) as which
                  from input i
                 ) i
           ) i
      where which = 'login'

需要更多子查询。问题是您要比较两个字段中的日期,因此最里面的子查询将它们放在一个字段中 ('thedate')。 next 查找下一个日期(当一行是登录时,它假定为注销,最外面的只是选择登录行。

说实话,考虑到您的数据结构,我认为我更喜欢带有子查询的第一个版本。

【讨论】:

  • 戈登,我使用 SQL Server2012。请修改查询并让我知道如何使用滞后功能。谢谢
  • @RizwanMalik 那为什么要使用sql-server-2008 标签?
  • 我现在已经标记了 Sql server 2012。请您修改查询,因为上面的查询没有给出任何结果
  • where which ... 子句放错了位置。
  • @AndriyM 。 . .不完全错位。它必须在lead() 之后(即“外部”),因此它不会影响受lead() 影响的行。但是,它确实导致了语法错误,因为我忘记在子查询中包含which
【解决方案2】:

当我必须对数据进行非规范化时,我喜欢使用自联接。我已经在 SQL Server 2012 上测试了以下内容。(但是,一般方法应该适用于任何支持自连接的 SQL RDBS,但您必须根据具体情况修改 DATETIME2 数据类型和函数调用。)

数据:

DECLARE @t1 AS TABLE (
    Day1 DATE
    ,Staff VARCHAR(3)
    ,Login DATETIME2
    ,Logout DATETIME2
);

INSERT INTO @t1
VALUES ('20130704', '123', '20130704 18:44:16.533', '20130704 00:00:00.000')
,('20130706', '456', '20130706 00:00:00.000', '20130706 01:10:12.000')
,('20130704', '123', '20130704 00:00:00.000', '20130704 20:24:16.553')
,('20130704', '123', '20130704 20:44:16.533', '20130704 00:00:00.000')
,('20130704', '123', '20130704 00:00:00.000', '20130704 22:54:16.553')
,('20130705', '456', '20130705 08:45:12.550', '20130705 00:00:00.000');

解决方案:

SELECT a.Day1, a.Staff, a.Login, MIN(b.Logout) AS Logout, 
    DateDiff(minute, a.login, MIN(b.logout)) AS Elapsed
FROM @t1 a join @t1 b 
    ON a.Staff = b.Staff AND a.Login < b.Logout AND CONVERT(TIME, a.Login) > '00:00:00.00'
GROUP BY  a.Day1, a.Staff, a.Login;

补充说明:

  1. 您提到您必须计算小时数。这可以在同一个查询中完成。在这种情况下,“已用”列计算分钟数。 (根据您的目的将DateDiff 的第一个参数更改为hour。)

@戈登林诺夫

  1. 如果登录是一天而注销是另一天,查询会正确处理情况。

【讨论】:

    猜你喜欢
    • 2013-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多