Get sum of qty based on hour window - sql

I'm working on a problem to figure out how much qty of product was created and dispatched between certain hours. For example, I need to see how much was created (by created I mean how many orders were created with X qty) between 6pm today and 3pm tomorrow. I'm trying to create a time bin for this but whatever I try isn't working out.
select CREATE_DATE
, CREATE_TIME
, RELEASED_DATE
, RELEASED_TIME
, sum(case
when CREATE_DATE = DATEADD(DAY, DATEDIFF(DAY, 0, CREATE_DATE), 0)
and CREATE_TIME >= '18:00:00' AND CREATE_DATE = DATEADD(DAY, DATEDIFF(DAY, 0, CREATE_DATE), 1)
then ORDER_QTY
when CREATE_DATE = DATEADD(DAY, DATEDIFF(DAY, 0, CREATE_DATE), 1)
and CREATE_TIME <= '14:59:59'
then ORDER_QTY
end) as small_window_qty
, sum(ORDER_QTY) as ord_qty
, sum(RELEASED_QTY) as rls_qty
from table
Any help with this would be appreciated. Just need a way to organize the days into the following buckets: Normal Hour Window = Created 6pm to 6pm; Smaller Hour Window = Created 6pm to 3pm; Agreed Upon = Dispatch by 3pm (12am to 3pm)
Edit: Some clarification. What im trying to accomplish is a root cause analysis. We have orders that create every day, and must ship within 2 days of being created. We're trying to figure out why our orders are not shipping on time. So as an RCA, Im trying to dig into the orders, when they were created, when they were dispatched(or released, same thing) and when they shipped. The Hour Window's mentioned above are cutoff times for orders to be created for a certain day. Example:
300 units were created today, and they must ship 2 days from now. I want to see, of that 300 created, how many were created before 3pm, and of that, how much dispatched by 3pm same day. Hope that clarifies things. Not everything that is created must be dispatched the same day, as we have 2 days to ship the orders.

you're leaving a lot of info out so i'm filling in the blanks and making assumptions here. doublecheck that my data types line up with your data types.
create table sales (create_date date, create_time time, released_date date, released_time time, qty int)
insert sales
select '1/14/13','18:45','1/15/13','10:45', 10
union all
select '1/14/13','19:45','1/15/13','12:45', 12
union all
select '1/15/13','19:15','1/16/13','16:45', 6
union all
select '1/15/13','18:00','1/16/13','14:45', 25
union all
select '1/15/13','18:45','1/16/13','15:00', 3
union all
select '1/16/13','19:45','1/17/13','16:30', 1
union all
select '1/16/13','20:45','1/17/13','17:45', 9
union all
select '1/17/13','18:30','1/18/13','18:00', 17
union all
select '1/18/13','18:30','1/19/13','17:15', 15
union all
select '1/18/13','18:45','1/19/13','19:15', 21
with base as
(
select *
, cast(create_date as datetime) + cast(create_time as datetime) as createtime
, cast(released_date as datetime) + cast(released_time as datetime) as releasetime
, datediff(hour,cast(create_date as datetime) + cast(create_time as datetime),cast(released_date as datetime) + cast(released_time as datetime)) as hrs
from sales
),
base2 as
(
select qty
, case
when create_time >= '18:00' and hrs <= 21 then 'small'
when create_time >= '18:00' and hrs <= 24 then 'normal'
else 'outside'
end as orderwindow
, case
when hrs between 6 and 21 then 'pass'
else 'fail'
end as agreedupon
from base
)
select sum(qty) as qty, orderwindow, agreedupon
from base2
group by orderwindow, agreedupon
drop table sales
this should give you the end result of being able to tell how much was created, what window of time it falls into, and if released by 3pm. adjust as needed.
i didn't want the code to get messy and convoluted so i used 2 CTEs.

I am not sure about your expected result and also your input data looks messay and not clear. So I used the data provided by the above answer. It looks you get running sum. If you can review the answer and get back to me if you need more modifications that is good. I suggest you to edit your Question and be more clear with your sample data and expected answer. Add more details to the question.
create table sales (create_date date, create_time time, released_date date, released_time time, qty int)
INSERT INTO sales
select '1/14/13','18:45','1/15/13','10:45', 10
union all
select '1/14/13','19:45','1/15/13','12:45', 12
union all
select '1/15/13','19:15','1/16/13','16:45', 6
union all
select '1/15/13','18:00','1/16/13','14:45', 25
union all
select '1/15/13','18:45','1/16/13','15:00', 3
union all
select '1/16/13','19:45','1/17/13','16:30', 1
union all
select '1/16/13','20:45','1/17/13','17:45', 9
union all
select '1/17/13','18:30','1/18/13','18:00', 17
union all
select '1/18/13','18:30','1/19/13','17:15', 15
union all
select '1/18/13','18:45','1/19/13','19:15', 21
SELECT
create_date
, create_time
, released_date
, released_time
,SUM(QTY) OVER ( PARTITION BY A.[Dispatched Window] ORDER BY released_date ) AS [Sum_qty]
,Qty
FROM
(
SELECT
create_date
, create_time
, released_date
, released_time
, CASE WHEN released_date BETWEEN CONVERT(DATETIME, CONVERT(CHAR(8), create_date, 112) + ' ' + CONVERT(CHAR(8), '18:00:00.0000000' , 108)) AND CONVERT(DATETIME, CONVERT(CHAR(8), DATEADD(DAY,1, create_date), 112) + ' ' + CONVERT(CHAR(8), '18:00:00.0000000' , 108)) THEN 'Normal Window'
WHEN released_date BETWEEN CONVERT(DATETIME, CONVERT(CHAR(8), create_date, 112) + ' ' + CONVERT(CHAR(8), '17:59:00.0000000' , 108)) AND CONVERT(DATETIME, CONVERT(CHAR(8), DATEADD(DAY,1, create_date), 112) + ' ' + CONVERT(CHAR(8), '15:00:00.0000000' , 108)) THEN 'Smaller Window'
WHEN released_date BETWEEN CONVERT(DATETIME, CONVERT(CHAR(8), create_date, 112) + ' ' + CONVERT(CHAR(8), '00:00:00.0000000' , 108)) AND CONVERT(DATETIME, CONVERT(CHAR(8), create_date, 112) + ' ' + CONVERT(CHAR(8), '15:00:00.0000000' , 108)) THEN 'Aggreed AUpon'
ELSE 'N/A'
END AS [Dispatched Window]
, Qty
FROM dbo.sales
) AS A

Related

distribute accumulated working hours through days

I have Date time when engine has started working and how long was it working. but sometimes it can work more than 24 Hours.
if it worked for 28 Hours on the starting date i will have record
Name started_working Finished working hours_worked
obj-00123 07/02/2018 13:30 08/02/2018 17:30 28
I need to to have record that will show that engine has worked for 10:30 in 07 and 17:30 in 08.
Name started_working Finished working hours_worked
obj-00123 07/02/2018 13:30 07/02/2018 00:00 10:30
obj-00123 07/02/2018 13:30 08/02/2018 17:30 17:30
or something like that. I don't have any idea how can i get this done. can you give me some clues. i dont ask for writing code if its not too easy.
thank you
This might do the trick for you
--Using CTE to show sample data
;WITH cteX( Name,started_working,Finished_working)
AS
(
SELECT
'obj-00123','07/02/2018 13:30','08/02/2018 17:30' UNION ALL
SELECT 'obj-00155','07/02/2018 15:00','07/02/2018 22:30'
)
SELECT
X.Name
, X.started_working
, X.Finished_working
, HoursWorked = CONVERT(VARCHAR(12), DATEADD(minute, DATEDIFF(minute, X.started_working, X.Finished_working), 0), 114)
FROM
(
SELECT
T1.Name
,T1.started_working
,Finished_working = DATEADD(SECOND,0,DATEADD(DAY, DATEDIFF(DAY,-1,T1.started_working),0)) -- Dummy finish time # Midnight
FROM
cteX T1
WHERE
DATEDIFF(DAY,T1.started_working,T1.Finished_working) <> 0 --Create a dummy finish time #Midnight when start and end not on same day
UNION ALL
SELECT
T2.Name
,started_working = CASE WHEN DATEDIFF(DAY,T2.started_working,T2.Finished_working) <> 0
THEN DATEADD(DAY, DATEDIFF(DAY, 0, T2.Finished_working), 0) --Start # Midnight
ELSE T2.started_working
END
,T2.Finished_working
FROM
cteX T2
) X
ORDER BY
X.Name, X.started_working
OUTPUT
Name started_working Finished_working HoursWorked
obj-00123 2018-07-02 13:30:00.000 2018-07-03 00:00:00.000 10:30:00:000
obj-00123 2018-08-02 00:00:00.000 2018-08-02 17:30:00.000 17:30:00:000
obj-00155 2018-07-02 15:00:00.000 2018-07-02 22:30:00.000 07:30:00:000
According to your sample data working hours may be more than several days. In this case you need to use tally table or recursive CTE. I have used recursive CTE since it's easier to handle result fields. Also there are two columns in result named started_working and started_working2. started_working is from your expected output, but I believe you need started_working2 column
declare #T as table (
Name varchar(100)
, started_working datetime
, finished_working datetime
--, hours_worked int
)
insert into #T
values
('obj-00123', '20180207 13:30', '20180208 17:30')
, ('obj-00123', '20180208 19:00', '20180209 05:00')
, ('obj-00123', '20180209 19:00', '20180209 22:00')
, ('obj-00123', '20180210 19:00', '20180213 22:00')
;with rcte as (
select
*, started_working2 = started_working
, next_date = cast(dateadd(dd, 1, started_working) as date), 1 step
from
#T
union all
select
Name, started_working, finished_working
, cast(next_date as datetime)
, dateadd(dd, 1, next_date), step + 1
from
rcte
where
next_date < finished_working
)
select
Name, started_working, started_working2, finished_working
, right(replace(str(diff / 60), ' ', 0), 2) + ':' + right(replace(str(diff % 60), ' ', 0), 2) hours_worked
from (
select
Name, started_working
, case
when step = 1 then started_working
else started_working2
end started_working2
, case
when step = max(step) over (partition by Name, started_working)
then finished_working else next_date
end finished_working
from
rcte
) t
cross apply (select datediff(mi, started_working2, finished_working) diff) ca
I'd approach the solution something like this:
WITH dynamic_twelths_of_hr_table(datetime2_value) AS
(
SELECT '2017-01-01'
UNION ALL
SELECT DATEADD(MINUTE, 5, datetime2_value)
FROM dynamic_twelths_of_hr_table
WHERE DATEADD(MINUTE, 5, datetime2_value) <= '2019-01-01'
)
,twelths_hr_table AS
(
SELECT
DATEADD(DAY, DATEDIFF(DAY, 0, datetime2_value), 0) AS date_value
,datetime2_value
FROM dynamic_twelths_of_hr_table
)
,modified_source_table AS
(
SELECT
name
,objectid
,engine_start
,ISNULL(engine_stop, GETDATE()) AS engine_stop
,IIF(engine_start IS NULL OR engine_stop IS NULL, 1, 0) AS is_still_running
FROM [YOUR_SOURCE_TABLE]
)
SELECT
name
,objectid
,is_still_running
,date_value
,(COUNT(datetime2_value)/12.0) AS hours_run_on_this_day
FROM
modified_source_table
LEFT JOIN
twelths_hr_table AS tht
ON (tht.datetime2_value BETWEEN engine_start AND engine_stop)
GROUP BY
name, objectid, is_still_running, date_value
ORDER BY
name, objectid, is_still_running, date_value
Note I haven't tested this code so please excuse any small syntax errors.
I've also baked in an assumption about the range of dates to be considered (these can be widened, or made dynamic based on when the query runs), and it has a 5 minute resolution (based on the fact that, at a glance, I could only see one value in the engine_stop column that didn't fall on a 5-minute threshold - so I assume sub-5-minute precision is not required).
Basically what it does is expand each engine row out into 5-minute windows (twelths of an hour), and then simply groups these by day and counts the number of windows per day during which the engine was running.
For currently-running engines, it will calculate how long it has run so far. I trust you can tweak the code to your exact requirements.
thank you to all. this worked perfectly. it needed slight polishing and recursion needed to be set to 0.
But creating view is a trouble with CTE.
create view mroobjectenginerowkinghoursdeclare as
declare #T as table (
Name nvarchar(100)
, OBJECTID varchar(50)
, started_working datetime
,STOPFROM datetime
,STARTDATE datetime
,STOPDATE datetime
,MODIFIEDDATETIME datetime
,START_STOP int
,STARTDESCRIPTION nvarchar(300)
,STOPDESCRIPTION nvarchar(300)
,wattage nvarchar (50)
,purpose nvarchar(300)
,location nvarchar(300)
,finished_working datetime
,oldDiff int
)
insert into #T
select
NAME
,OBJECTID
,STOPTO
,STOPFROM
,STARTDATE
,STOPDATE
,MODIFIEDDATETIME
,START_STOP
,STARTDESCRIPTION
,STOPDESCRIPTION
,wattage
,purpose
,location
,next_stopfrom
,diff
FROM [MicrosoftDynamicsAX].[dbo].[mroobjectengineworkinghours]
;with rcte as (
select
*, started_working2 = started_working
, next_date = cast(dateadd(dd, 1, started_working) as date), 1 step
from
#T
union all
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location, finished_working,oldDiff
, cast(next_date as datetime)
, dateadd(dd, 1, next_date), step + 1
from
rcte
where
next_date < finished_working
)
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location,oldDiff, started_working2, finished_working
, right(replace(str(diff / 60), ' ', 0), 2) + ':' + right(replace(str(diff % 60), ' ', 0), 2) hours_worked
from (
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location,oldDiff
, case
when step = 1 then started_working
else started_working2
end started_working2
, case
when step = max(step) over (partition by Name, started_working)
then finished_working else next_date
end finished_working
from
rcte
) t
cross apply (select datediff(mi, started_working2, finished_working) diff) ca
OPTION (MAXRECURSION 0);

Distribute amount monthly with respect to start date and end date

I have table as
Project_ID
Start_Date
End_Date
BUDGET_Amount
For example:
I Need to Return with SQL 12 Row Each Row Represent the Month-Year between the Two Date and the Value of Budget = 1200 / No of months between two dates "12" = 100$
So The Result to be like this
Proj_ID , START_DATE , END_DATE , AMOUNT
"1","1-JAN-2017","31-JAN-2017",100$
"1","1-FEB-2017","27-FEB-2017",100$m
"1","1-MAR-2017","31-MAR-2017",100$
"1","1-APR-2017","31-APR-2017",100$
"1","1-MAY-2017","31-MAY-2017",100$
"1","1-JUN-2017","31-JUN-2017",100$
"1","1-JUL-2017","31-JUL-2017",100$
"1","1-AUG-2017","31-AUG-2017",100$
"1","1-SEP-2017","31-SEP-2017",100$
"1","1-OCT-2017","31-OCT-2017",100$
"1","1-NOV-2017","31-NOV-2017",100$
"1","1-DEC-2017","31-DEC-2017",100$
Using Common Table Expression(CTE) you can generate dates in given range. This will generate the output you need:
;with mycte as
(
select cast('1 jan 2017' as datetime) as DateValue
union all
select DATEADD(MONTH,1, DateValue)
from mycte
where DATEADD(MONTH,1, DateValue) <= '31 dec 2017'
)
select
1 Proj_ID,
REPLACE(CONVERT(VARCHAR(11), DateValue, 106), ' ', '-') START_DATE ,
REPLACE(CONVERT(VARCHAR(11), DATEADD(DAY, -1, DATEADD(MONTH,1, DateValue))), ' ', '-') END_DATE ,
'100$' AMOUNT
from mycte
This is will display months' first and last days.

How to convert sql dates range to Daily Row

How do I convert a date range so each day is 1 row with the start and end time for that day?
I looked at this post about date ranges to row - but this is a different problem. The other solution linked above does not give the time from start to finish for each day - and thus does not allow for duty factor or utilization calculations, and or the build of a Gantt chart.
We would have an ID field, a Start Date, and an End Date as our base table. We want to convert this to contain the ID Field per day with how much time was consumed in that range.
This is very useful when converting a start and end dates to daily duty factor and a host of other needs like equipment utilization.
I had a lot of help from this community figuring this out. I wanted to put the final SQL script here for others to use as well.
WITH cte ([VID],[StartTime],[EndTime]) AS
( SELECT tbl.[ID] as 'VID'
,CONVERT(VARCHAR(19), tbl.[StartDT], 120) AS 'StartTime'
,CASE
WHEN tbl.[EndDT] <= CONVERT(VARCHAR(11), tbl.[StartDT]+1, 120) + '00:00:00' THEN CONVERT(VARCHAR(19), tbl.[EndDT], 120)
ELSE CONVERT(VARCHAR(11), tbl.[StartDT]+1, 120) + '00:00:00'
END as 'EndTime'
FROM [SourceTable] as tbl
WHERE DATEDIFF(DAY,tbl.[StartDT],tbl.[EndDT] )<=365
UNION ALL
SELECT tbl.[ID] as 'VID'
,CONVERT(VARCHAR(11), DATEADD(DAY, 1, cte.[StartTime]), 120) + '00:00:00' AS 'StartTime'
,CASE
WHEN CONVERT(VARCHAR(19), tbl.[EndDT], 120) < CONVERT(VARCHAR(11), DATEADD(DAY, 2, cte.[StartTime]), 120) + '00:00:00'
THEN CONVERT(VARCHAR(19), tbl.[EndDT], 120)
ELSE CONVERT(VARCHAR(11), DATEADD(DAY, 2, cte.[StartTime]), 120) + '00:00:00'
END AS 'EndTime'
FROM cte
INNER JOIN [SourceTable] as tbl
ON cte.VID = tbl.ID
WHERE CONVERT(VARCHAR(11), cte.[StartTime], 120) < CONVERT(VARCHAR(11), tbl.[EndDT], 120))
SELECT VID AS ID
,[StartTime]
,[EndTime]
,DateDiff (second,[StartTime],[EndTime]) / 3600 As 'Hours'
,DateDiff (second,[StartTime],[EndTime])/60 % 60 as 'Minutes'
,((DateDiff (Second,[StartTime],[EndTime]) / 3600)*60)+(DateDiff (second,starttime,endtime)/60 % 60) as 'Total Minutes'
,DATEPART(week,[StartTime]) AS weeknum
,MONTH([StartTime]) AS MonthNumber
,DATENAME(month,[StartTime]) AS MonthName
FROM cte order by Id, [StartTime]
option (maxrecursion 0);

Grouping by DATEPART(hh,TimeStamp) to show nightshift, how do I see data for hours past midnight?

I'm importing my data to excel and so I need to see the date as a varchar to use for a graph in excel but I also need the data for the individual hours in the day as well. My manager wants to see data for the past hour whenever he checks my chart. This is my code so far. Dayshift has been fine but I can't get the hours to go past 24 for nightshift so I can't group them in my graph in excel.
convert(VARCHAR, TimeStamp, 101) as date
,StationID as lane
,DATEPART(hh,TimeStamp)
,.185 as posSD1
,-.185 as negSD1
,.370 as posSD2
,-.370 as negSD2
,.556 as posSD3
,-.556 as negSD3
, COUNT (TrickleActual) as Count
, convert(decimal (18,3) ,AVG (TrickleActual - TrickleTarget)) as Average
FROM CherryBoxInfo
WHERE TimeStamp >= '2015-05-01' -- '2015-05-01 18:30:00'
and TimeStamp between convert(DATETIME, convert(VARCHAR, TimeStamp, 101) + ' ' + '19:00:00') and convert(DATETIME, convert(VARCHAR, DATEADD(day, 1, TimeStamp), 101) + ' ' + '04:30:00')
and (TrickleActual-TrickleTarget) BETWEEN -1 and 1
GROUP BY
convert(VARCHAR, TimeStamp, 101)
,StationID
,DATEPART(hh,TimeStamp)
ORDER BY convert(VARCHAR, TimeStamp, 101)
,StationID
,DATEPART(hh,TimeStamp)
Instead of grouping on datepart, group on date truncated to hour.
Here is how to truncate a date:
select dateadd(hh, datediff(hh, 0, #dt), 0)

Sales counts by week for individual items sold

I have a large SQL table that contains, among other fields, the following
Item, Date Sold,
1 Day Coaster 2014-11-10,
3 Day Coaster 2014-02-16,
1 Day Coaster 2014-11-11,
AC-Zip 2014-12-21,
5 Day Package 2014-05-15,
1 Day Coaster 2014-11-07,
Being new to SQL, my expertise can only select the items individually and take the counts from individual results and type them into excel from the rows affected result.
I need to be able to pull the count of individual items sold by week and list the counts by week into an excel spreadsheet thus;
Week Item Count
2014-11-07-2014-11-13 1 Day Coaster 3
You need to use a Group BY statement.
here is a sample SQLFiddle, it might get you started: SQL Fiddle
You can use this :
SELECT A.Item,A.DateRange,COUNT(A.Item)
FROM (
SELECT Item,
(SELECT DISTINCT
CASE
WHEN YEAR(DATEADD(DAY, 1-DATEPART(WEEKDAY, Min(Date_Sold)), Min(Date_Sold))) < YEAR(Min(Date_Sold))
THEN
CAST(DATEADD(YEAR, DATEDIFF(YEAR, 0,DATEADD(YEAR, 0 ,GETDATE())), 0) AS Varchar(50))
+ ' TO ' + Cast(DATEADD(dd, 7-(DATEPART(dw, Min(Date_Sold))), Min(Date_Sold)) AS Varchar(50))
ELSE
Cast(DATEADD(DAY, 1-DATEPART(WEEKDAY, Min(Date_Sold)), Min(Date_Sold)) AS Varchar(50))
+ ' TO ' + Cast(DATEADD(dd, 7-(DATEPART(dw, Min(Date_Sold))), Min(Date_Sold)) AS Varchar(50))
END AS DateRange
FROM <YOUR_TABLE> B
WHERE A.Date_Sold=B.Date_Sold
AND A.Item=B.Item
group by Item
) AS DateRange
FROM <YOUR_TABLE> A
) A
GROUP BY Item,DateRange;
Let me know if this is not what you are looking at.
USe this. Fiddler Demo
Output:
Query
CREATE TABLE weekdays
(
datevalue datetime NOT NULL
, Item VARCHAR(MAX)
);
INSERT INTO weekdays (datevalue, item) VALUES
('2014-11-10', '1 Day Coaster'),
('2014-02-16', '2 Day Coaster'),
('2014-11-11', '1 Day Coaster'),
('2014-12-21', 'AC-Zip'),
('2014-05-15', '5 Day Package'),
('2014-11-07', '1 Day Coaster');
CREATE FUNCTION [dbo].[ufn_GetFirstDayOfWeek]
( #pInputDate DATETIME )
RETURNS DATETIME
BEGIN
SET #pInputDate = CONVERT(VARCHAR(10), #pInputDate, 111)
RETURN DATEADD(DD, 1 - DATEPART(DW, #pInputDate),
#pInputDate)
END
GO
SET DATEFIRST 5
SELECT CONVERT(VARCHAR(10), A.Date, 101) + ' - ' +
CONVERT(VARCHAR(10), DATEADD(d, 6, A.Date), 101) AS Week,
A.Item,
COUNT(A.Item) AS COUNT FROM
(SELECT [dbo].ufn_GetFirstDayOfWeek(datevalue) AS Date, item FROM weekdays) AS A
GROUP BY A.Date, A.Item