0

我有下表,其中包含所有人员的进出时间:

 CREATE TABLE test (
       timecardid INT
     , trandate DATE
     , employeeid INT
     , trantime TIME
     , Trantype VARCHAR(1)
     , Projcode VARCHAR(3)
)

任务是获取所有带有 trantype A(可能使用 MIN)的最早 trantime 和带有 trantype Z(使用 Max)的最新 trantime,所有这些都在该 trandate 中(即 7 月 17 日的 trantype A 是 8:00 AM 和 trantype 7 月 17 日的 Z 是晚上 7:00)。

问题是,输出的格式应该与它来自的表的格式相同,这意味着我必须保留这些数据并过滤掉其余数据(不是该日期最早和最晚的输入/输出,每员工)

我目前的解决方案是使用两个不同的选择命令来获取所有最早的,然后获取所有最新的。然后将它们结合起来。

不过我想知道,有没有更简单的单字符串解决方案?

非常感谢。

编辑(我很抱歉,这里是示例。服务器是 SQL Server 2008):

 Timecardid | Trandate | employeeid | trantime | trantype | Projcode 
      1      2013-04-01      1        8:00:00        A       SAMPLE1
      2      2013-04-01      1        9:00:00        A       SAMPLE1
      3      2013-04-01      2        7:00:00        A       SAMPLE1
      4      2013-04-01      2        6:59:59        A       SAMPLE1
      5      2013-04-01      1       17:00:00        Z       SAMPLE1
      6      2013-04-01      1       17:19:00        Z       SAMPLE1
      7      2013-04-01      2        17:00:00       Z       SAMPLE1

      8      2013-04-02      1        8:00:00        A       SAMPLE1
      9      2013-04-02      1        9:00:00        A       SAMPLE1
     10      2013-04-02      2        7:00:58        A       SAMPLE1
     11      2013-04-02      2       18:00:00        Z       SAMPLE1
     12      2013-04-02      2       18:00:01        Z       SAMPLE1
     13      2013-04-02      1       20:00:00        Z       SAMPLE1

预期结果(在选择命令中,每个员工每天最早进出和最晚出出):

  Timecardid | Trandate | employeeid | trantime | trantype | Projcode 
      1      2013-04-01      1        8:00:00        A       SAMPLE1 
      4      2013-04-01      2        6:59:59        A       SAMPLE1   
      6      2013-04-01      1       17:19:00        Z       SAMPLE1
      7      2013-04-01      2        17:00:00       Z       SAMPLE1
      8      2013-04-02      1        8:00:00        A       SAMPLE1
     10      2013-04-02      2        7:00:58        A       SAMPLE1
     12      2013-04-02      2       18:00:01        Z       SAMPLE1
     13      2013-04-02      1       20:00:00        Z       SAMPLE1

非常感谢

4

4 回答 4

2

也许这就是您正在寻找的:

select 
    t.* 
from 
    test t
where 
    trantime in (
        (select min(trantime) from test t1 where t1.trandate = t.trandate and trantype = 'A'),
        (select max(trantime) from test t2 where t2.trandate = t.trandate and trantype = 'Z')
    )

更改我的答案以考虑“每位员工”的要求:

;WITH EarliestIn AS
(
    SELECT trandate, employeeid, min(trantime) AS EarliestTimeIn
    FROM test 
    WHERE trantype = 'A'
    GROUP BY trandate, employeeid 
), 
LatestOut AS
(
    SELECT trandate, employeeid, max(trantime) AS LatestTimeOut
    FROM test 
    WHERE trantype = 'Z'
    GROUP BY trandate, employeeid 
)
SELECT * 
FROM test t
WHERE 
    EXISTS (SELECT * FROM EarliestIn WHERE t.trandate = EarliestIn.trandate AND t.employeeid = EarliestIn.employeeid AND t.trantime = EarliestIn.EarliestTimeIn) 
    OR EXISTS (SELECT * FROM LatestOut WHERE t.trandate = LatestOut.trandate AND t.employeeid = LatestOut.employeeid AND t.trantime = LatestOut.LatestTimeOut)
于 2013-07-01T07:12:57.103 回答
1

假设 timecardid 列是 PK 或唯一的,如果我理解正确,我会做类似的事情

DECLARE @date DATE
SET @date = '2013-07-01'

SELECT
    T0.*
FROM
    (SELECT DISTINCT employeeid FROM test) E
    CROSS APPLY (
        SELECT TOP 1
            T.timecardid
        FROM
            test T
        WHERE
            T.trandate = @date
            AND T.Trantype = 'A'
            AND T.employeeid = E.employeeid
        ORDER BY T.trantime
        UNION ALL
        SELECT TOP 1
            T.timecardid
        FROM
            test T
        WHERE
            T.trandate = @date
            AND T.Trantype = 'Z'
            AND T.employeeid = E.employeeid
        ORDER BY T.trantime DESC
    ) V
    JOIN test T0 ON T0.timecardid = V.timecardid

如果您了解性能,则应为表设置适当的索引。

于 2013-07-01T07:21:25.267 回答
1

我会ROW_NUMBER用来整理您要选择的行:

;with Ordered as (
  select *,
    ROW_NUMBER() OVER (PARTITION BY Trandate,employeeid,trantype
        ORDER BY trantime ASC) as rnEarly,
    ROW_NUMBER() OVER (PARTITION BY Trandate,employeeid,trantype
        ORDER BY trantime DESC) as rnLate
  from
    Test
)
select * from Ordered
where
  (rnEarly = 1 and trantype='A') or
  (rnLate = 1 and trantype='Z')
order by TimecardId

( SQLFiddle )

它会产生您所要求的结果,我认为它的可读性很强。trantype包含在PARTITION BY子句中的原因是AZ值接收单独的编号。

于 2013-07-01T09:07:23.193 回答
1

如果您使用的是 SQL Server 2012,则可以使用 LAG/LEAD 以相当简洁的方式查找最大和最小行;

WITH cte AS (
  SELECT *,
       LAG(timecardid)  OVER (PARTITION BY trandate,employeeid,trantype ORDER BY trantime)  lagid,
       LEAD(timecardid) OVER (PARTITION BY trandate,employeeid,trantype ORDER BY trantime) leadid
  FROM test
)
SELECT timecardid,trandate,employeeid,trantime,trantype,projcode
FROM cte
WHERE trantype='A' AND lagid  IS NULL
   OR trantype='Z' AND leadid IS NULL;

一个用于测试的 SQLfiddle

于 2013-07-01T09:17:24.223 回答