Create Computed column w.r.t to a column value occurance

时间:2015-10-29 15:45:21

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

I have a table that gives me the details of the employees check-in and check-out details.

+------------+-------------------------+----------+
| EmployeeId |        PunchTime        | Category |
+------------+-------------------------+----------+
|        201 | 2015-10-14 23:30:00.000 | SHIFT    |
|        201 | 2015-10-14 23:30:00.000 | TIN      |
|        201 | 2015-10-15 01:17:00.000 | CBRK     |
|        201 | 2015-10-15 01:30:00.000 | PBRK1    |
|        201 | 2015-10-15 07:43:00.000 | CBRK     |
|        201 | 2015-10-15 23:30:00.000 | SHIFT    |
|        201 | 2015-10-15 23:30:00.000 | TIN      |
|        201 | 2015-10-16 02:47:00.000 | CBRK     |
|        201 | 2015-10-16 03:45:00.000 | UBRK     |
|        201 | 2015-10-16 07:44:00.000 | CBRK     |
|        201 | 2015-10-16 23:30:00.000 | SHIFT    |
|        201 | 2015-10-16 23:30:00.000 | TIN      |
|        201 | 2015-10-17 00:30:00.000 | CBRK     |
|        201 | 2015-10-17 01:00:00.000 | UISP     |
|        201 | 2015-10-17 01:30:00.000 | PBRK1    |
|        201 | 2015-10-17 03:30:00.000 | PBRK2    |
+------------+-------------------------+----------+

Here the category value "SHIFT" would determine that the employee's first check-in of his/her shift for the day. At times there can be shifts that span across days.(i.e that beings at 23:30 on 14th to 07:43 on the 15th).

So a particular days shift would be from the First Shift row up to the next shift row. (i.e The shift of the user on the 14th would be from row 1 to row 5)

So I need to project out the Shift Date w.r.t to the first check-in date irrespective of the day the shift is ending.

This would be my desired output.

+-------------+-------------------------+----------+------------------+
| EmpoloyeeId |        PunchTime        | Category | Shift Date       |
+-------------+-------------------------+----------+------------------+
|         201 | 2015-10-14 23:30:00.000 | SHIFT    | 2015-10-14       |
|         201 | 2015-10-14 23:30:00.000 | TIN      | 2015-10-14       |
|         201 | 2015-10-15 01:17:00.000 | CBRK     | 2015-10-14       |
|         201 | 2015-10-15 01:30:00.000 | PBRK1    | 2015-10-14       |
|         201 | 2015-10-15 07:43:00.000 | CBRK     | 2015-10-14       |
|         201 | 2015-10-15 23:30:00.000 | SHIFT    | 2015-10-15       |
|         201 | 2015-10-15 23:30:00.000 | TIN      | 2015-10-15       |
|         201 | 2015-10-15 23:32:00.000 | LCV      | 2015-10-15       |
|         201 | 2015-10-16 02:47:00.000 | CBRK     | 2015-10-15       |
|         201 | 2015-10-16 03:45:00.000 | UBRK     | 2015-10-15       |
|         201 | 2015-10-16 07:44:00.000 | CBRK     | 2015-10-15       |
|         201 | 2015-10-16 23:30:00.000 | SHIFT    | 2014-10-16       |
|         201 | 2015-10-16 23:30:00.000 | TIN      | 2014-10-16       |
|         201 | 2015-10-17 00:30:00.000 | CBRK     | 2014-10-16       |
|         201 | 2015-10-17 01:00:00.000 | UISP     | 2014-10-16       |
|         201 | 2015-10-17 01:30:00.000 | PBRK1    | 2014-10-16       |
|         201 | 2015-10-17 03:30:00.000 | PBRK2    | 2014-10-16       |
+-------------+-------------------------+----------+------------------+

I tried to project out a column that would group my shifts w.r.t to the row-number (using case then when) but i will be only able to assign value to the row where the Shift starts. the rest of my columns would be the value of the else case. like

+-------------+-------------------------+----------+--------+
| EmpoloyeeId |        PunchTime        | Category | row    |
+-------------+-------------------------+----------+--------+
|         201 | 2015-10-14 23:30:00.000 | SHIFT    | 1      |
|         201 | 2015-10-14 23:30:00.000 | TIN      | 0      |
|         201 | 2015-10-15 01:17:00.000 | CBRK     | 0      |
|         201 | 2015-10-15 01:30:00.000 | PBRK1    | 0      |
|         201 | 2015-10-15 07:43:00.000 | CBRK     | 0      |
|         201 | 2015-10-15 23:30:00.000 | SHIFT    | 6      |
|         201 | 2015-10-15 23:30:00.000 | TIN      | 0      |
|         201 | 2015-10-15 23:32:00.000 | LCV      | 0      |
|         201 | 2015-10-16 02:47:00.000 | CBRK     | 0      |
|         201 | 2015-10-16 03:45:00.000 | UBRK     | 0      |
|         201 | 2015-10-16 07:44:00.000 | CBRK     | 0      |
|         201 | 2015-10-16 23:30:00.000 | SHIFT    | 11     |
|         201 | 2015-10-16 23:30:00.000 | TIN      | 0      |
|         201 | 2015-10-17 00:30:00.000 | CBRK     | 0      |
|         201 | 2015-10-17 01:00:00.000 | UISP     | 0      |
|         201 | 2015-10-17 01:30:00.000 | PBRK1    | 0      |
|         201 | 2015-10-17 03:30:00.000 | PBRK2    | 0      |
+-------------+-------------------------+----------+--------+

I have added the sample data in sql fiddle

The query i have reached so far is

SELECT *, CASE WHEN Category = 'Shift' THEN (
                ROW_NUMBER() OVER (
                    ORDER BY EmployeeId, PunchTime
                    )
                ) ELSE '0' END AS Spearator
FROM [zzzEmpShift]
WHERE EmployeeId = 201 

2 个答案:

答案 0 :(得分:2)

这适用于2008年和2012年

<强> SQL Fiddle Demo

WITH row_id as (
    SELECT *, ROW_NUMBER() over (partition by [EmployeeId] order by [PunchTime]) rn
    FROM zzzEmpShift
), shift as (
   SELECT [EmployeeId], rn, cast([PunchTime] As Date) as [Shift Date]
   FROM row_id
   WHERE [Category] = 'SHIFT'
)
SELECT *
FROM row_id R 
JOIN shift S
  ON R.[EmployeeId] = S.[EmployeeId]
WHERE S.rn  = (SELECT MAX(T.rn) FROM shift T WHERE T.rn <= R.rn)

<强>输出

╔═════╦═════════════════════════╦═══════╦════╦════╦════════════╗
║ 201 ║ 2015-10-14 23:30:00.000 ║ SHIFT ║  1 ║  1 ║ 2015-10-14 ║
║ 201 ║ 2015-10-14 23:30:00.000 ║ TIN   ║  2 ║  1 ║ 2015-10-14 ║
║ 201 ║ 2015-10-15 01:17:00.000 ║ CBRK  ║  3 ║  1 ║ 2015-10-14 ║
║ 201 ║ 2015-10-15 01:30:00.000 ║ PBRK1 ║  4 ║  1 ║ 2015-10-14 ║
║ 201 ║ 2015-10-15 07:43:00.000 ║ CBRK  ║  5 ║  1 ║ 2015-10-14 ║
║ 201 ║ 2015-10-15 23:30:00.000 ║ SHIFT ║  6 ║  6 ║ 2015-10-15 ║
║ 201 ║ 2015-10-15 23:30:00.000 ║ TIN   ║  7 ║  6 ║ 2015-10-15 ║
║ 201 ║ 2015-10-16 02:47:00.000 ║ CBRK  ║  8 ║  6 ║ 2015-10-15 ║
║ 201 ║ 2015-10-16 03:45:00.000 ║ UBRK  ║  9 ║  6 ║ 2015-10-15 ║
║ 201 ║ 2015-10-16 07:44:00.000 ║ CBRK  ║ 10 ║  6 ║ 2015-10-15 ║
║ 201 ║ 2015-10-16 23:30:00.000 ║ SHIFT ║ 11 ║ 11 ║ 2015-10-16 ║
║ 201 ║ 2015-10-16 23:30:00.000 ║ TIN   ║ 12 ║ 11 ║ 2015-10-16 ║
║ 201 ║ 2015-10-17 00:30:00.000 ║ CBRK  ║ 13 ║ 11 ║ 2015-10-16 ║
║ 201 ║ 2015-10-17 01:00:00.000 ║ UISP  ║ 14 ║ 11 ║ 2015-10-16 ║
║ 201 ║ 2015-10-17 01:30:00.000 ║ PBRK1 ║ 15 ║ 11 ║ 2015-10-16 ║
║ 201 ║ 2015-10-17 03:30:00.000 ║ PBRK2 ║ 16 ║ 11 ║ 2015-10-16 ║
╚═════╩═════════════════════════╩═══════╩════╩════╩════════════╝

这是2012 + SqlFiddleDemo

的版本
WITH group_id as (
    SELECT *, SUM(case when Category ='Shift' then 1 else 0 end) 
                  OVER (partition by [EmployeeId] order by [PunchTime]) rn
    FROM zzzEmpShift
)
SELECT *, MIN(cast([PunchTime] As Date)) OVER (partition by rn) as [Shift Date]
FROM group_id

答案 1 :(得分:1)

To get the rows for the same shift grouped together, you can use a running total of this:

(case when Category ='Shift' then 1 else 0 end) 

If you're using SQL Server 2012 (or newer) you can just use sum with over clause. With older versions, it might be easiest (and maybe even fastest) just to use temp table + cursor. Other option is to do a query back to the table to calculate the rows with "Shift" that have smaller time, but that causes quite a lot of IO.