如何为特定员工提供进出时间?

时间:2018-01-11 08:51:20

标签: sql sql-server

我的表格如下:

id       time_stamp                 Access Type    
1001    2017-09-05 09:35:00         IN
1002    2017-09-05 11:00:00         IN
1001    2017-09-05 12:00:00         OUT
1002    2017-09-05 12:25:00         OUT
1001    2017-09-05 13:00:00         IN
1002    2017-09-05 14:00:00         IN
1001    2017-09-05 17:00:00         OUT
1002   2017-09-05 18:00:00          OUT

我在下面尝试了这个查询:

SELECT ROW_NUMBER() OVER (
        ORDER BY A.emp_reader_id ASC
        ) AS SNo
    ,B.emp_code
    ,B.emp_name
    ,CASE 
        WHEN F.event_entry_name = 'IN'
            THEN A.DT
        END AS in_time
    ,CASE 
        WHEN F.event_entry_name = 'OUT'
            THEN A.DT
        END AS out_time
    ,cast(left(CONVERT(TIME, a.DT), 5) AS VARCHAR) AS 'time'
    ,isnull(B.areaname, 'OAE6080036073000006') AS areaname
    ,C.dept_name
    ,b.emp_reader_id
    ,isnull(c.dept_name, '') AS group_name
    ,CONVERT(CHAR(11), '2017/12/30', 103) AS StartDate
    ,CONVERT(CHAR(11), '2018/01/11', 103) AS ToDate
    ,0 AS emp_card_no
FROM dbo.trnevents AS A
LEFT OUTER JOIN dbo.employee AS B ON A.emp_reader_id = B.emp_reader_id
LEFT OUTER JOIN dbo.departments AS C ON B.dept_id = C.dept_id
LEFT OUTER JOIN dbo.DevicePersonnelarea AS E ON A.POINTID = E.areaid
LEFT OUTER JOIN dbo.Event_entry AS F ON A.EVENTID = F.event_entry_id
ORDER BY A.emp_reader_id ASC

它有效,但需要如下所示。有时在事件和事件中有相同的内容:

SNo emp_code    emp_name    in_time                     out_time                    time    areaname    dept_name   emp_reader_id   group_name  StartDate   ToDate      emp_card_no
1   102         Ihsan Titi  NULL                        2017-12-30 12:16:26.000     12:16   Dubai       Sales       102             Sales       2017/12/30  2018/01/11  0
2   102         Ihsan Titi  NULL                        2017-12-30 12:16:27.000     12:16   Dubai       Sales       102             Sales       2017/12/30  2018/01/11  0
3   102         Ihsan Titi  2017-12-30 12:44:26.000     NULL                        12:44   Dubai       Sales       102             Sales       2017/12/30  2018/01/11  0
4   102         Ihsan Titi  2017-12-30 16:27:48.000     NULL                        16:27   Dubai       Sales       102             Sales       2017/12/30  2018/01/11  0

预期输出:

SNo emp_code    emp_name        in_time                     out_time                    time    areaname    dept_name   emp_reader_id   group_name  StartDate   ToDate      emp_card_no
1   102         Ihsan Titi      2017-12-30 12:16:26.000     2017-12-30 12:44:26.000     12:16   Dubai       Sales       102             Sales       2017/12/30  2018/01/11  0
2   102         Ihsan Titi      2017-12-30 12:50:26.000     2017-12-30 16:27:48.000     12:16   Dubai       Sales       102             Sales       2017/12/30  2018/01/11  0 

请帮助我坚持到这里来这样......

3 个答案:

答案 0 :(得分:1)

你可以用这个:

select  A_In.emp_reader_id as empId,A_In.Belongs_to,A_In.DeviceSerialNumber,
        DT as EntryTime,
        (
         select min(DT) as OutTime
         from   trnevents A_Out
         where  EVENTID like 'IN'
         and    A_Out.emp_reader_id = A_In.emp_reader_id
         and    A_Out.DT > A_In.DT and DATEDIFF(day,A_In.Dt,A_Out.DT)=0
        ) as ExitTime from trnevents A_In   where    EVENTID like 'OUT'

    from    trnevents A_In

答案 1 :(得分:0)

我在下面找到它的方式是说,如果一个事件与之前的事件类型相同,那么将其视为一个"流氓"。

盗贼总是独自坐着,永远不会与任何其他事件配对。

所有其他事件都已配对,IN是第一项,OUT是第二项。

然后我可以将所有内容分组以将对减少到单行。

WITH
  rogue_check
AS
(
  SELECT
    CASE WHEN LAG(F.event_entry_name) OVER (PARTITION BY A.emp_reader_number ORDER BY A.DT) = F.event_entry_name THEN 1 ELSE 0 END   AS is_rogue,
    *
  FROM
    trnevents     AS A
  LEFT JOIN
    EVent_entry   AS F
      ON  F.event_entry_id = A.event_id
),
  sorted AS
(
  SELECT
    ROW_NUMBER() OVER (                                         ORDER BY DT)   AS event_sequence_id,
    ROW_NUMBER() OVER (PARTITION BY emp_reader_number, is_rogue ORDER BY DT)   AS employee_checked_event_sequence_id,
    *
  FROM
    rogue_check
)
SELECT
  MIN(event_sequence_id)                                    AS unique_id,
  emp_reader_number,
  MAX(CASE WHEN event_entry_name = 'IN'  THEN DT END)       AS time_in,
  MAX(CASE WHEN event_entry_name = 'OUT' THEN DT END)       AS time_out
FROM
  sorted
GROUP BY
  emp_reader_number,
  is_rogue,
  employee_checked_event_sequence_id - CASE WHEN is_rogue = 1 OR event_entry_name = 'IN' THEN 0 ELSE 1 END
ORDER BY
  emp_reader_number,
  unique_id
;

示例架构:

CREATE TABLE trnevents (
  emp_reader_number    INT,
  DT                   DATETIME,
  event_id             INT
);

CREATE TABLE Event_entry (
  event_entry_id       INT,
  event_entry_name     NVARCHAR(32)
);

示例数据:

INSERT INTO Event_entry VALUES (0, N'IN'), (1, N'OUT');

INSERT INTO trnevents VALUES
  (1, '2017-01-01 08:00', 0),
  (1, '2017-01-01 08:01', 0),
  (1, '2017-01-01 12:00', 1),
  (1, '2017-01-01 13:00', 0),
  (1, '2017-01-01 17:00', 1),
  (1, '2017-01-01 17:01', 1)
;

示例结果:

unique_id   emp_reader_number   time_in               time_out
1           1                   01/01/2017 08:00:00   01/01/2017 12:00:00
2           1                   01/01/2017 08:01:00   null
4           1                   01/01/2017 13:00:00   01/01/2017 17:00:00
6           1                   null                  01/01/2017 17:01:00

GROUP BY比我在火车上预期的更加繁琐,因此可能会导致大型数据集的执行计划中出现SORT。我很快就会想到另一种选择。

这是一个带有一些简单虚拟数据的演示,证明它至少适用于那些情况。 (如果他们发现任何问题,请随意更新其他案例)

http://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=d06680d8ed374666760cdc67182aaacb

答案 2 :(得分:-2)

您可以使用PIVOT

 select id, [in], out
 from
 ( select 
      id, time_stamp, accessType, 
     (ROW_NUMBER() over (partition by id order by time_stamp) -1 )/ 2 rn   
   from yourtable ) src
 pivot 
 (min(time_stamp) for accessType in ([in],[out])) p

这假设每个" in"之后是" out"并使用row_number对这些时间组进行分组。