Reputation: 597
I couldn't come up with a better title.
I have a table with this structure:
id | timestamp | barcode
I need to get the count for each product for each day and shift with one query.
Product
is left(barcode, 9)
, date
should come from the timestamp
, which is datetime
and shift
should come from timestamp as well. Shifts are as follows:
Shift 1 : 06:00:00 to 14:29:59
Shift 2 : 14:30:00 to 22:59:59
Shift 3 : 23:00:00 to 05:59:59
So I basically need a result like:
date | shift | item number | count
12.02.2019 | 1 | 827384950 | 32
So Far I have this:
select cast(timestamp as date) as date,
(case when convert(time, timestamp) >= '06:00:00' and
convert(time, timestamp) < '14:30:00'
then 1
when convert(time, timestamp) >= '14:30:00' and
convert(time, timestamp) < '23:00:00'
then 2
else 3
end) as shift,
left(barcode, 9) as item_number,
count(*)
from t
group by cast(timestamp as date),
(case when convert(time, timestamp) >= '06:00:00' and
convert(time, timestamp) < '14:30:00'
then 1
when convert(time, timestamp) >= '14:30:00' and
convert(time, timestamp) < '23:00:00'
then 2
else 3
end),
left(barcode, 9)
order by date, shift, item_number;
It does the job, but the problem is this:
Shift 3 is actually taking part in two days, since it is from 23:00 on one day until 6 on the next day. This code splits shift 3 to two parts - one for each day.
I need to group it so Shift 3 shows for the day it started and then counts it's item count until it finishes on the next day.
Upvotes: 1
Views: 30
Reputation: 2229
If you have some sample data to test with, that would be great!
Essentially, I am shifting the timestamp back 6 hours to avoid the confusion of the cross-day 3rd shift. It pulls the full 3rd shift to the date in which the shift starts while not losing the date of the 1st shift (since 00:00 counts for the date).
SELECT TheDay = TRY_CONVERT(DATE, DATEADD(HOUR, -6, t.[timestamp])),
Shift = CASE
WHEN TRY_CONVERT(TIME, DATEADD(HOUR, -6, t.[timestamp])) < '08:30'
THEN 1
WHEN TRY_CONVERT(TIME, DATEADD(HOUR, -6, t.[timestamp])) < '17:00'
THEN 2
ELSE 3
END,
Product = LEFT(t.[barcode], 9),
ItemCount = COUNT(*)
FROM [TableName] t
GROUP BY TRY_CONVERT(DATE, DATEADD(HOUR, -6, t.[timestamp])),
CASE
WHEN TRY_CONVERT(TIME, DATEADD(HOUR, -6, t.[timestamp])) < '08:30'
THEN 1
WHEN TRY_CONVERT(TIME, DATEADD(HOUR, -6, t.[timestamp])) < '17:00'
THEN 2
ELSE 3
END,
LEFT(t.[barcode], 9)
@elizabk makes a good point:
;WITH shiftedData (TheDay, Shift, Product) AS (
SELECT TheDay = TRY_CONVERT(DATE, DATEADD(HOUR, -6, t.[timestamp])),
Shift = CASE
WHEN TRY_CONVERT(TIME, DATEADD(HOUR, -6, t.[timestamp])) < '08:30'
THEN 1
WHEN TRY_CONVERT(TIME, DATEADD(HOUR, -6, t.[timestamp])) < '17:00'
THEN 2
ELSE 3
END,
Product = LEFT(t.[barcode], 9)
FROM [TableName] t
)
SELECT TheDay, Shift, Product, ProductCount = COUNT(*)
FROM shiftedData
GROUP BY TheDay, Shift, Product
Upvotes: 0
Reputation: 480
I like to keep complicated logic out of GROUP BY
clauses, this way when someone else comes to read the code they can clearly grasp the logic of the grouping. I instead separate out the logic into a SELECT
statement of a CTE, and use the resulting columns in a simple readable query.
;WITH cte AS
(
SELECT id, LEFT(barcode, 9) item_number,
CONVERT(DATE, CASE WHEN CONVERT(TIME, timestamp) <'06:00:00' THEN DATEADD(DAY, -1, timestamp) ELSE timestamp END) AS date,
CASE WHEN CONVERT(TIME, timestamp) >= '06:00:00' AND
CONVERT(TIME, timestamp) < '14:30:00'
THEN 1
WHEN CONVERT(TIME, timestamp) >= '14:30:00' AND
CONVERT(TIME, timestamp) < '23:00:00'
THEN 2
ELSE 3
END AS shift
FROM Table t
)
SELECT date, shift, item_number, count(*) as count
FROM cte
GROUP BY date, shift, item_number
Upvotes: 3
Reputation: 1193
Please try this:
SELECT CASE WHEN a.shift = 3 AND a.[time] < '06:00' THEN DATEADD(DAY,-1,a.[date]) ELSE a.[date] END AS [date]
,a.shift,a.item_number,COUNT(*)
FROM (
SELECT TRY_CONVERT(DATE,t.[timestamp]) AS [date]
,TRY_CONVERT(TIME,t.timestamp) AS [time]
,CASE
WHEN TRY_CONVERT(TIME,t.timestamp) >= '23:00' OR TRY_CONVERT(TIME,t.timestamp) < '06:00' THEN 3
WHEN TRY_CONVERT(TIME,t.timestamp) >= '14:30' THEN 2
ELSE 1
END AS [shift]
,LEFT(t.barcode,9) AS [item_number]
FROM [YourTableName] t
) a
GROUP BY CASE WHEN a.shift = 3 AND a.[time] < '06:00' THEN DATEADD(DAY,-1,a.[date]) ELSE a.[date] END
,a.shift,a.item_number
;
Upvotes: 0