Date and Time field query - sql-server

If Date and Time field is having date entry like '2017-12-04 21:30:00.000' and Duration is '1800' and '2017-12-04 21:30:22.000' has '22' then our query should show record as '2017-12-04 21:30:00.000' as '1800' but 22 seconds should come at '2017-12-04 21:30:00.000' but my query is showing at 2017-12-04 21:00:00.000 with Duration as '1822' (sum of 1800+22). I have this question is this link in different manner but didn't get a proper answer hence asking this question here as I'm not able to modify in proper way.
Query:
SELECT Interval=(CASE WHEN datepart(MINUTE,[DateTime]) = 0 and datepart(SECOND,DateTime)=0 THEN
CAST(CONVERT(VARCHAR(30),DATEADD(HOUR,-1,[DateTime]),101) + ' '+ cast(format(DATEPART(HOUR,DATEADD(HOUR,-1,[DateTime])),'0#') as varchar)+':30:00' as DateTime)
ELSE (CAST(CONVERT(VARCHAR(30),[DateTime],101) +' ' + (case when datepart(MINUTE,[DateTime])<=30 and
datepart(SECOND,[DateTime])<59
then cast(format(DATEPART(HOUR,[DateTime]),'0#') as varchar)+':00:00'
else cast(format(DATEPART(HOUR,[DateTime]),'0#') as varchar)+':30:00' end) as DateTime)) END),
ID,Code,Duration=SUM(Duration) FROM Table
WHERE [DateTime] >= '2017-12-04 00:00:00' and [DateTime] <= '2017-12-04 23:59:59'
GROUP BY (CASE WHEN datepart(MINUTE,[DateTime]) = 0 and datepart(SECOND,DateTime)=0 THEN
CAST(CONVERT(VARCHAR(30),DATEADD(HOUR,-1,[DateTime]),101) + ' '+ cast(format(DATEPART(HOUR,DATEADD(HOUR,-1,[DateTime])),'0#') as varchar)+':30:00' as DateTime)
ELSE (CAST(CONVERT(VARCHAR(30),[DateTime],101) +' ' + (case when datepart(MINUTE,[DateTime])<=30 and
datepart(SECOND,[DateTime])<59 then cast(format(DATEPART(HOUR,[DateTime]),'0#') as varchar)+':00:00'
else cast(format(DATEPART(HOUR,[DateTime]),'0#') as varchar)+':30:00' end) as DateTime)) END),
ID,Code
Order by Interval
Actual table data.
DateTime ID Code Duration
2017-12-12 00:30:00 1 12 1800
2017-12-12 00:30:37 1 12 37
2017-12-12 01:00:00 1 12 1793
2017-12-12 01:30:00 1 12 1800
2017-12-12 01:30:59 1 12 59
If I run the query then expected result is,
DateTime ID Code Duration
2017-12-12 00:00:00 1 12 1837
2017-12-12 00:30:00 1 12 1800
2017-12-12 01:00:00 1 12 1800
2017-12-12 01:30:00 1 12 59
DateTime ID Code Duration
2017-12-12 00:00:00 1 12 1800
2017-12-12 00:30:00 1 12 1800
2017-12-12 01:00:00 1 12 1800
2017-12-12 01:30:00 1 12 59

You can use date functions to round [DateTime] into 30 minute intervals, but the result does not match your expected result, which appears to be wrong
DateTime ID Code Duration
2017-12-12 00:30:00 1 12 1800 1800 + 337 = 1837 2017-12-12 00:30
2017-12-12 00:30:37 1 12 37
2017-12-12 01:00:00 1 12 1793 1793 2017-12-12 01:00
2017-12-12 01:30:00 1 12 1800 1800 + 59 = 1859 2017-12-12 01:30
2017-12-12 01:30:59 1 12 59
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE Table1
([DateTime] datetime, [ID] int, [Code] int, [Duration] int)
;
INSERT INTO Table1
([DateTime], [ID], [Code], [Duration])
VALUES
('2017-12-12 00:30:00', 1, 12, 1800),
('2017-12-12 00:30:37', 1, 12, 37),
('2017-12-12 01:00:00', 1, 12, 1793),
('2017-12-12 01:30:00', 1, 12, 1800),
('2017-12-12 01:30:59', 1, 12, 59)
;
Query 1:
select
dateadd(minute, (datediff(minute, [DateTime], 0) / 30) * 30, 0)
, sum(duration)
from table1
group by
dateadd(minute, (datediff(minute, [DateTime], 0) / 30) * 30, 0)
Results:
| | |
|----------------------|------|
| 1782-01-19T22:30:00Z | 1859 |
| 1782-01-19T23:00:00Z | 1793 |
| 1782-01-19T23:30:00Z | 1837 |

Related

How to increment a column like 1, 1, 2, 2, 3, 3 in SQL Server

I have data like this in a table:
https://i.stack.imgur.com/JYZjz.png
I want to group first two events (1, 10) as F1, than second two events (1,10) as F2
Running this SQL query:
SELECT
Id, Date, Event,
CASE
WHEN Event = 1 AND LEAD(Event) OVER (ORDER BY ID) = 10
THEN 'F1'
WHEN Event = 10 AND LAG(Event) OVER (ORDER BY ID) = 1
THEN 'F1'
ELSE NULL
END AS Flow
FROM
lxfr.SystemEventsDiary
WHERE
Event IN (1, 10)
I get this result as output:
ID
Date
Event
Flow
4
2022-02-07 00:00:00.000
1
F1
44
2022-02-08 00:00:00.000
10
F1
47
2022-02-09 00:00:00.000
1
F1
72
2022-02-10 00:00:00.000
10
F1
75
2022-02-10 00:00:00.000
1
F1
97
2022-02-11 00:00:00.000
10
F1
100
2022-02-11 00:00:00.000
1
NULL
113
2022-02-28 00:00:00.000
1
F1
217
2022-03-04 00:00:00.000
10
F1
235
2022-03-22 00:00:00.000
10
NULL
241
2022-03-22 00:00:00.000
1
F1
270
2022-03-24 00:00:00.000
10
F1
273
2022-03-24 00:00:00.000
1
F1
308
2022-03-25 00:00:00.000
10
F1
But I need a result like this:
ID
Date
Event
Flow
4
2022-02-07 00:00:00.000
1
F1
44
2022-02-08 00:00:00.000
10
F1
47
2022-02-09 00:00:00.000
1
F2
72
2022-02-10 00:00:00.000
10
F2
75
2022-02-10 00:00:00.000
1
F3
97
2022-02-11 00:00:00.000
10
F3
100
2022-02-11 00:00:00.000
1
NULL
113
2022-02-28 00:00:00.000
1
F4
217
2022-03-04 00:00:00.000
10
F4
235
2022-03-22 00:00:00.000
10
NULL
241
2022-03-22 00:00:00.000
1
F5
270
2022-03-24 00:00:00.000
10
F5
273
2022-03-24 00:00:00.000
1
F6
308
2022-03-25 00:00:00.000
10
F6
I tried with a CTE, but I'm getting errors...
Thanks
This seems to work. I've copied your desired output as my input sample data just to show that my new column matches your Desired one, and also a couple of other renames for keywords:
declare #t table (ID int, DateOf datetime, EventNo int, DesiredFlow varchar(7))
insert into #t(ID,DateOf,EventNo,DesiredFlow) values
(4 ,'2022-02-07T00:00:00.000',1 ,'F1'),
(44 ,'2022-02-08T00:00:00.000',10 ,'F1'),
(47 ,'2022-02-09T00:00:00.000',1 ,'F2'),
(72 ,'2022-02-10T00:00:00.000',10 ,'F2'),
(75 ,'2022-02-10T00:00:00.000',1 ,'F3'),
(97 ,'2022-02-11T00:00:00.000',10 ,'F3'),
(100 ,'2022-02-11T00:00:00.000',1 ,NULL),
(113 ,'2022-02-28T00:00:00.000',1 ,'F4'),
(217 ,'2022-03-04T00:00:00.000',10 ,'F4'),
(235 ,'2022-03-22T00:00:00.000',10 ,NULL),
(241 ,'2022-03-22T00:00:00.000',1 ,'F5'),
(270 ,'2022-03-24T00:00:00.000',10 ,'F5'),
(273 ,'2022-03-24T00:00:00.000',1 ,'F6'),
(308 ,'2022-03-25T00:00:00.000',10 ,'F6')
;With Neighbours as (
select
*,
LEAD(EventNo,1,NULL) OVER (ORDER BY ID) as Successor,
LAG(EventNo,1,NULL) OVER (ORDER BY ID) as Predecessor
from
#t t
), NoStragglers as (
select
*
from
Neighbours
where
(EventNo = 1 and Successor = 10) or
(EventNo = 10 and Predecessor = 1)
), Ordered as (
select
*, ROW_NUMBER() OVER (PARTITION BY EventNo ORDER BY ID) as rn
from NoStragglers
)
select
t.*,'F' + (CONVERT(varchar(10),o.rn)) as ActualFlow
from
#t t
left join
Ordered o
on
t.Id = o.Id
Hopefully you can see how each CTE builds from the previous one (either by inspection or changing the final select to pull one one of the earlier CTEs instead).
Result:
ID DateOf EventNo DesiredFlow ActualFlow
----------- ----------------------- ----------- ----------- -----------
4 2022-02-07 00:00:00.000 1 F1 F1
44 2022-02-08 00:00:00.000 10 F1 F1
47 2022-02-09 00:00:00.000 1 F2 F2
72 2022-02-10 00:00:00.000 10 F2 F2
75 2022-02-10 00:00:00.000 1 F3 F3
97 2022-02-11 00:00:00.000 10 F3 F3
100 2022-02-11 00:00:00.000 1 NULL NULL
113 2022-02-28 00:00:00.000 1 F4 F4
217 2022-03-04 00:00:00.000 10 F4 F4
235 2022-03-22 00:00:00.000 10 NULL NULL
241 2022-03-22 00:00:00.000 1 F5 F5
270 2022-03-24 00:00:00.000 10 F5 F5
273 2022-03-24 00:00:00.000 1 F6 F6
308 2022-03-25 00:00:00.000 10 F6 F6
Which seems to match.
Check this,
create table #temp(id int,dates datetime,Events int)
insert into #temp(id,dates,events) values
(4 ,'2022-02-07 00:00:00.000', 1 )
,(44 ,'2022-02-08 00:00:00.000', 10 )
,(47 ,'2022-02-09 00:00:00.000', 1 )
,(72 ,'2022-02-10 00:00:00.000', 10 )
,(75 ,'2022-02-10 00:00:00.000', 1 )
,(97 ,'2022-02-11 00:00:00.000', 10 )
,(100 ,'2022-02-11 00:00:00.000', 1 )
,(113 ,'2022-02-28 00:00:00.000', 1 )
,(217 ,'2022-03-04 00:00:00.000', 10 )
,(235 ,'2022-03-22 00:00:00.000', 10 )
,(241 ,'2022-03-22 00:00:00.000', 1 )
,(270 ,'2022-03-24 00:00:00.000', 10 )
,(273 ,'2022-03-24 00:00:00.000', 1 )
,(308 ,'2022-03-25 00:00:00.000', 10 )
WITH CTE
AS (SELECT Id,
Dates,
Events,
CASE
WHEN Events = 1
AND LEAD(Events) OVER(
ORDER BY ID) = 10
THEN id
WHEN Events = 10
AND LAG(Events) OVER(
ORDER BY ID) = 1
THEN LAG(id) OVER(
ORDER BY ID)
ELSE NULL
END AS Flow
FROM #temp),
CtE1
AS (SELECT *,
DENSE_RANK() OVER(
ORDER BY flow) rn
FROM CTe
WHERE flow IS NOT NULL
UNION ALL
SELECT *,
NULL
FROM CTE
WHERE flow IS NULL)
SELECT id,
dates,
events,
CASE
WHEN rn IS NOT NULL
THEN concat('F', rn)
ELSE NULL
END Flow
FROM cte1
ORDER BY id;
drop table #temp
You can use cascading subqueries to assign a ROW_NUMBER() based on Flow values that are NOT NULL:
SELECT d.Id,
d.Date,
d.Event,
CASE WHEN b.rn IS NULL THEN NULL
ELSE CONCAT('F', b.rn)
END AS Flow
FROM (SELECT
Id,
Date,
Event,
Flow,
ROW_NUMBER() OVER (PARTITION BY Event ORDER BY Id ASC) as rn
FROM (SELECT
Id,
Date,
Event,
CASE WHEN Event = 1 AND LEAD(Event) OVER (ORDER BY Id) = 10 THEN 1
WHEN Event = 10 AND LAG(Event) OVER (ORDER BY Id) = 1 THEN 1
ELSE 0
END as Flow
FROM SystemEventsDiary WHERE Event IN (1, 10)
) a WHERE Flow = 1) b
RIGHT JOIN SystemEventsDiary d ON b.id = d.Id
ORDER BY d.Id ASC
Result:
Id
Date
Event
Flow
4
2022-02-07 00:00:00.000
1
F1
44
2022-02-08 00:00:00.000
10
F1
47
2022-02-09 00:00:00.000
1
F2
72
2022-02-10 00:00:00.000
10
F2
75
2022-02-10 00:00:00.000
1
F3
97
2022-02-11 00:00:00.000
10
F3
100
2022-02-11 00:00:00.000
1
NULL
113
2022-02-28 00:00:00.000
1
F4
217
2022-03-04 00:00:00.000
10
F4
235
2022-03-22 00:00:00.000
10
NULL
241
2022-03-22 00:00:00.000
1
F5
270
2022-03-24 00:00:00.000
10
F5
273
2022-03-24 00:00:00.000
1
F6
308
2022-03-25 00:00:00.000
10
F6
db<>fiddle here.

Subtract depletion And Limit for SUM

First I'm using AdventureWork2019 as a reference
I have a query where I'm joining 5 Tables
USE [AdventureWorks2019]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Alter PROCEDURE dbo.TestLocation
#UseDate DateTime
AS
BEGIN
SET NOCOUNT ON;
SELECT prodID
,SUM(PurchQty) AS TotalPurchase
,SUM(SalesQty) AS TotalSell
,StartDate
from (
SELECT DISTINCT WO.ProductID AS prodID
, StartDate
,WO.OrderQty AS PurchQty
,SOD.OrderQty AS SalesQty
FROM Sales.SalesOrderDetail SOD
LEFT JOIN Production.WorkOrderRouting WOR ON WOR.ProductID = SOD.ProductID
--LEFT JOIN Production.Location PL ON PL.LocationID = WOR.LocationID
--The above Join is the one for the locationID and it's working Fine
LEFT JOIN Production.WorkOrder WO ON WO.ProductID = SOD.ProductID
FULL OUTER JOIN Purchasing.PurchaseOrderDetail POD ON POD.ProductID = SOD.ProductID
WHERE StartDate = #UseDate
-- AND PL.LocationID >= 10
) Test3
Group by prodID,StartDate
order by prodID ASC, StartDate
END
GO
EXEC TestLocation '2011-07-02 00:00:00.000'
Output(sample):
prodID TotalPurc TotalSell StartDate
717 8 36 2011-07-02 00:00:00.000
730 9 47 2011-07-02 00:00:00.000
744 2 3 2011-07-02 00:00:00.000
747 12 21 2011-07-02 00:00:00.000
749 5 15 2011-07-02 00:00:00.000
761 16 138 2011-07-02 00:00:00.000
775 26 91 2011-07-02 00:00:00.000
777 12 78 2011-07-02 00:00:00.000
802 6 21 2011-07-02 00:00:00.000
804 40 60 2011-07-02 00:00:00.000
806 16 138 2011-07-02 00:00:00.000
807 24 23 2011-07-02 00:00:00.000
810 21 28 2011-07-02 00:00:00.000
811 6 21 2011-07-02 00:00:00.000
813 8 37 2011-07-02 00:00:00.000
817 21 28 2011-07-02 00:00:00.000
And another Table For LocationID (as a warehouse)
SELECT LocationID,CostRate,Availability
FROM Production.Location
WHERE LocationID >= 10
order by CostRate ASC
LocationID CostRate Availability
50 12.25 120.00
60 12.25 120.00
30 14.50 120.00
40 15.75 120.00
45 18.00 80.00
10 22.50 96.00
20 25.00 108.00
What I want to do is to take each LoactionId and ProdID and take TotalPurc to the location and decrement the quantity in the Availability column, each TotalSell will increment the Availability column. The max Availability quantity is 130.
If all locations have no Available quantity that is the Available is 0 for all locations then it will stop.
the above will work with the date specified as you can check the query and run it if you have
AdventureWork2019
simple output to check how I want the data to be:
prodID TotalPurc TotalSell StartDate
717 8 36 2011-07-02 00:00:00.000
730 9 47 2011-07-02 00:00:00.000
744 2 3 2011-07-02 00:00:00.000
747 12 21 2011-07-02 00:00:00.000
749 5 15 2011-07-02 00:00:00.000
LocationID CostRate Availability
50 12.25 120.00
60 12.25 120.00
30 14.50 120.00
40 15.75 120.00
45 18.00 80.00
10 22.50 96.00
20 25.00 108.00
Output :
prodID TotalPurc TotalSell StartDate LocationID Availability Remaining
717 8 36 2011-07-02 00:00:00.000 50 130 18
717 8 36 2011-07-02 00:00:00.000 60 130 8
717 8 36 2011-07-02 00:00:00.000 30 128 0
--what happened above is that I took the (120-8) = 112 then 112+36 = 148 we only can use 130 then the remaining is 18 then we took the next `LocationID` with the least Cost (120+18 = 138 we can use 130 so we took the 8) and used it in the next `LocationID`
730 9 47 2011-07-02 00:00:00.000 30 130 36
730 9 47 2011-07-02 00:00:00.000 40 130 26
730 9 47 2011-07-02 00:00:00.000 45 106 0
744 2 3 2011-07-02 00:00:00.000 45 107 0
747 12 21 2011-07-02 00:00:00.000 45 116 0
749 5 15 2011-07-02 00:00:00.000 45 126 0
--the above is the same as the first 3 rows we subtract and add to the availability
The other condition is that if all locations reached 0 or 130 then stop
How can I do that in SQL Server? I tried using CTE but didn't work well with me and tried the cursor which I think is the best for this kind of thing but didn't achieve anything.
Thank you in advance
Edit :
ALTER FUNCTION GetStockMovment
(
-- Add the parameters for the function here
#ForDate Datetime
)
RETURNS #Sums TABLE (
RemoveQTY Numeric(24, 7),
ADDQTY Numeric(24, 7)
)
AS
BEGIN
Declare #WoSum Numeric(24, 7),
#SODSUM Numeric(24, 7),
#WORSum Numeric(24, 7),
#PODSum Numeric(24, 7)
select #SODSUM = SUM(SOD.OrderQty) from Sales.SalesOrderDetail SOD
INNER JOIN Sales.SalesOrderHeader SOH ON SOD.SalesOrderID = SOH.SalesOrderID
where SOH.OrderDate = #ForDate
select #WoSum = sum(orderQty) from Production.WorkOrder
where StartDate = #ForDate
select #PODSum = sum(POD.OrderQty) from Purchasing.PurchaseOrderDetail POD
INNER JOIN Purchasing.PurchaseOrderHeader POH ON POD.PurchaseOrderID = POH.PurchaseOrderID
where POH.OrderDate = #ForDate
select #WoSum = sum(WO.OrderQty) from Production.WorkOrder WO
where WO.DueDate = #ForDate
INSERT INTO #Sums (RemoveQTY,ADDQTY)
SELECT isnull(#SODSUM,0) + isnull(#WORSum,0) , isnull(#PODSum,0) + isnull(#WoSum,0)
RETURN;
END;
GO
select * from dbo.GetStockMovment ('2014-05-26 00:00:00.000')
Output:
RemoveQTY ADDQTY
189.0000000 5334.0000000
You should use LAG or LEAD function.
https://learn.microsoft.com/en-us/sql/t-sql/functions/lead-transact-sql?view=sql-server-ver15
https://learn.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql?view=sql-server-ver15

Oracle - Cycle detected while executing recursive 'WITH' query

I'm doing a basic example of recursive query with oracle sql. I'm computing future months of the format MON-YY. I managed to have a seemingly correct query but I don't understand the break condition with a WITH query.
I'm trying to break on the year value (for example stop when you reach 2020), but it detects a cycle while doing that. If I break on the month value (e.g. December), it works.
Here's my query with a month based break:
with
prochains_mois(mois, annee) as (
select 'sep' as mois, 19 as annee
from dual
union all
select
case mois
when 'jan' then 'fev'
when 'fev' then 'mar'
when 'mar' then 'avr'
when 'avr' then 'mai'
when 'mai' then 'jun'
when 'jun' then 'jui'
when 'jui' then 'aou'
when 'aou' then 'sep'
when 'sep' then 'oct'
when 'oct' then 'nov'
when 'nov' then 'dec'
when 'dec' then 'jan'
end,
case mois
when 'dec' then annee + 1
else annee
end
from prochains_mois r
where mois <> 'dec'
)
select * from prochains_mois;
If I do this, it returns a consistent result.
MOI ANNEE
--- ----------
sep 19
oct 19
nov 19
dec 19
Now if I try to break the recursive query on the year, let's say 2020, so I change the where condition in the with clause to :
where annee < 20
Then I get :
ORA-32044: cycle detected while executing recursive WITH query
I tried to break with a later month to see if my year addition works correctly, it seems to be the case. If I break on march, I get the January and February correctly :
where mois <> 'mar'
gives
MOI ANNEE
--- ----------
sep 19
oct 19
nov 19
dec 19
jan 20
fev 20
mar 20
Use DATEs:
with prochains_mois( value ) as (
select DATE '2019-09-01' from dual
union all
select ADD_MONTHS( value, 1 )
FROM prochains_mois
WHERE value < DATE '2020-12-01'
)
select SUBSTR( TO_CHAR( value, 'mon', 'NLS_DATE_LANGUAGE=FRENCH' ), 1, 3 ) AS mois,
TO_CHAR( value, 'RR' ) AS annee
from prochains_mois;
Output:
MOIS | ANNEE
:--- | :----
sep | 19
oct | 19
nov | 19
dec | 19
jan | 20
fev | 20
mar | 20
avr | 20
mai | 20
jui | 20
jui | 20
aou | 20
sep | 20
oct | 20
nov | 20
dec | 20
or use your query and check that the month and year do not match:
with
prochains_mois(mois, annee) as (
select 'sep' as mois, 19 as annee
from dual
union all
select
case mois
when 'jan' then 'fev'
when 'fev' then 'mar'
when 'mar' then 'avr'
when 'avr' then 'mai'
when 'mai' then 'jun'
when 'jun' then 'jui'
when 'jui' then 'aou'
when 'aou' then 'sep'
when 'sep' then 'oct'
when 'oct' then 'nov'
when 'nov' then 'dec'
when 'dec' then 'jan'
end,
case mois
when 'dec' then annee + 1
else annee
end
from prochains_mois r
where ( mois, annee ) NOT IN ( ( 'dec', 20 ) )
)
select * from prochains_mois;
Output:
MOIS | ANNEE
:--- | ----:
sep | 19
oct | 19
nov | 19
dec | 19
jan | 20
fev | 20
mar | 20
avr | 20
mai | 20
jun | 20
jui | 20
aou | 20
sep | 20
oct | 20
nov | 20
dec | 20
db<>fiddle here
Your main issue is that you're trying to manipulate dates using strings/numbers. Don't do that; if you're working with dates, use dates!
E.g. you can do what you're after like so:
WITH prochains_mois (mnth_dt) AS (SELECT TRUNC(sysdate, 'mm') mnth_dt
FROM dual
UNION ALL
SELECT add_months(mnth_dt, 1) mnth_dt
FROM prochains_mois
WHERE add_months(mnth_dt, 1) < add_months(TRUNC(sysdate, 'yyyy'), 12))
SELECT mnth_dt,
to_char(mnth_dt, 'mon') mois,
to_char(mnth_dt, 'yy') annee
FROM prochains_mois;
MNTH_DT MOIS ANNEE
----------- ---- -----
01/09/2019 sep 19
01/10/2019 oct 19
01/11/2019 nov 19
01/12/2019 dec 19
N.B. You could simplify the predicate in the recursive sub-factored query to mnth_dt < add_months(TRUNC(SYSDATE, 'yyyy'), 11).
This works by taking the start date (here, I've used sysdate) and finding the first of the month (by using the optional second parameter of TRUNC to specify the level we're truncating it to).
Then we simply add a month to each date until we hit the last month of the start date's year.
Only after you've got the dates do you then output the data in the format you require using to_char.

Join table on a date between two dates in the target table

I have two tables
tbl_TimeEntries
EmployeeID int,
StartDateTime datetime,
EndDateTime datetime
tbl_Crew_Employees
CrewID,
EmployeeID,
StartDate,
EndDate
I also have a query that produces the number of hours worked per employee per day, but I also want to include the crew the employee was on for that day.
SELECT tbl_TimeEntries.EmployeeID,
SUM(DATEDIFF(SECOND, StartDateTime, EndDateTime) / 60.0
/ 60.0) as Hours,
CAST(StartDateTime AS date) as WorkDate
FROM tbl_TimeEntries
GROUP BY tbl_TimeEntries.EmployeeID, CAST(StartDateTime AS date)
ORDER BY CAST(StartDateTime AS date)
I'm not sure how to include the CrewID in this query because the tbl_Crew_Employees uses a StartDate and EndDate (meaning the employee was on this crew from StartDate to EndDate). I would either need to expand the StartDate/EndDate range or use some sort of SQL magic of which I am unaware.
Here is a sample of the data from the tbl_Crew_Employees, tbl_TimeEntries and the current query with the desired column data added. EmployeeID 88 is represented on two different crews in the sample.
CrewID EmployeeID StartDate EndDate
13 11 2013-03-30 2013-05-12
12 88 2013-01-02 2013-04-18
12 66 2013-01-02 2013-06-30
13 88 2013-04-19 2013-04-21
11 111 2013-01-02 2013-04-28
EmployeeID StartDateTime EndDateTime
11 2013-04-18 08:00 2013-04-18 12:00
11 2013-04-18 12:30 2013-04-18 18:30
111 2013-04-18 10:00 2013-04-18 12:00
111 2013-04-18 12:30 2013-04-18 18:30
88 2013-04-18 11:00 2013-04-18 12:00
88 2013-04-18 12:30 2013-04-18 19:30
66 2013-04-18 10:00 2013-04-18 12:00
66 2013-04-18 12:30 2013-04-18 18:30
11 2013-04-20 08:00 2013-04-20 12:00
11 2013-04-20 12:30 2013-04-20 18:00
111 2013-04-20 10:00 2013-04-20 12:00
111 2013-04-20 12:30 2013-04-20 18:30
88 2013-04-20 11:00 2013-04-20 12:00
88 2013-04-20 12:30 2013-04-20 19:30
66 2013-04-20 10:00 2013-04-20 12:00
66 2013-04-20 12:30 2013-04-20 17:00
EmployeeID Hours WorkDate CrewID(desired)
11 10.00 2013-04-18 13
88 8.00 2013-04-18 12
66 8.00 2013-04-18 12
111 8.00 2013-04-18 11
11 7.50 2013-04-20 13
88 8.00 2013-04-20 13
66 6.50 2013-04-20 12
111 8.00 2013-04-20 11
Try this:
SELECT
tbl_TimeEntries.employeeid
,SUM(DATEDIFF(SECOND, StartDateTime, EndDateTime) / 60.0 / 60.0) AS HOURS
,CAST(StartDateTime AS DATE) AS WorkDate
,tbl_Crew_Employees.crewid
FROM tbl_TimeEntries
INNER JOIN tbl_Crew_Employees ON tbl_timeentries.employeeid = tbl_Crew_Employees.employeeid
AND startdatetime >= startdate
AND enddatetime <= enddate
GROUP BY tbl_TimeEntries.employeeid
,tbl_Crew_Employees.crewid
,CAST(tbl_TimeEntries.StartDateTime AS DATE)
ORDER BY WorkDate
Should be a simple join.
declare #tbl_Crew_Employees table(CrewID int, EmployeeID int, StartDate date, EndDate date)
insert into #tbl_Crew_Employees
values
(13,11,'2013-03-30','2013-05-12'),
(12,88,'2013-01-02','2013-04-18'),
(12,66,'2013-01-02','2013-06-30'),
(13,88,'2013-04-19','2013-04-21'),
(11,111,'2013-01-02','2013-04-28')
declare #tbl_TimeEntries table (EmployeeID int, StartDateTime datetime, EndDateTime datetime)
insert into #tbl_TimeEntries
values
(11,'2013-04-18 08:00','2013-04-18 12:00'),
(11,'2013-04-18 12:30','2013-04-18 18:30'),
(111,'2013-04-18 10:00','2013-04-18 12:00'),
(111,'2013-04-18 12:30','2013-04-18 18:30'),
(88,'2013-04-18 11:00','2013-04-18 12:00'),
(88,'2013-04-18 12:30','2013-04-18 19:30'),
(66,'2013-04-18 10:00','2013-04-18 12:00'),
(66,'2013-04-18 12:30','2013-04-18 18:30'),
(11,'2013-04-20 08:00','2013-04-20 12:00'),
(11,'2013-04-20 12:30','2013-04-20 18:00'),
(111,'2013-04-20 10:00','2013-04-20 12:00'),
(111,'2013-04-20 12:30','2013-04-20 18:30'),
(88,'2013-04-20 11:00','2013-04-20 12:00'),
(88,'2013-04-20 12:30','2013-04-20 19:30'),
(66,'2013-04-20 10:00','2013-04-20 12:00'),
(66,'2013-04-20 12:30','2013-04-20 17:00')
SELECT
t.EmployeeID,
c.CrewID,
SUM(DATEDIFF(SECOND, t.StartDateTime, t.EndDateTime) / 60.0
/ 60.0) ,
CAST(t.StartDateTime AS date)
FROM #tbl_TimeEntries t
INNER JOIN
#tbl_Crew_Employees c on
c.EmployeeID = t.EmployeeID
and c.StartDate <= cast(t.StartDateTime as date)
and c.EndDate >= cast(t.EndDateTime as date)
GROUP BY t.EmployeeID, CAST(t.StartDateTime AS date), c.CrewID
ORDER BY CAST(t.StartDateTime AS date)

Using SQL (or T-SQL), How to get the length of Time a value was 0 or 1 each hour from a table that records the date and time a value was set?

I have a table with the following data:
StartTime Value
----------------------------------------------
2016-12-20 08:09:00 1
2016-12-20 09:53:00 0
2016-12-20 11:10:21 1
2016-12-20 11:30:00 1
2016-12-20 12:20:30 0
.... ....
.... ....
2016-12-20 23:44:10 1
2016-12-21 00:33:00 0
2016-12-21 01:05:05 0
So, what I'm trying to do is find how long a value was 0 or 1 during each hour, where the hours are from 00:00:00 - 01:00:00, then from 01:00:00 - 02:00:00 ... 23:00:00 - 00:00:00.
what I want to achieve looks like this:
HourStart Period Value Was 0 Period Value Was 1
--------------------------------------------------------------------------
2016-12-20 00:00:00 50 min 10 min
2016-12-20 01:00:00 30 30
2016-12-20 02:00:00 32 28
2016-12-20 03:00:00 60 00
2016-12-20 04:00:00 21 39
.... .... ....
.... .... ....
2016-12-20 21:00:00 30 30
2016-12-20 22:00:00 20 40
2016-12-20 23:00:00 10 50
2016-12-21 00:00:00 01 59
2016-12-21 01:00:00 30 30
.... ....
By the way, I'm using Microsoft Transact-SQL.
Thanks.
Here is one way to do this:
First, create and populate sample table (Please save us this step in your future questions)
DECLARE #T as TABLE
(
startTime datetime,
value int
)
INSERT INTO #T VALUES
('2016-12-20 08:09:00', 1),
('2016-12-20 09:53:00', 0),
('2016-12-20 11:10:21', 1),
('2016-12-20 11:30:00', 1),
('2016-12-20 12:20:30', 0),
('2016-12-20 23:44:10', 1),
('2016-12-21 00:33:00', 0),
('2016-12-21 01:05:05', 0)
Then, use a couple of common table expressions - one to get rid of the minutes and seconds of the start time, and the second one to get the start time of the previous row:
;WITH cte1 as
(
SELECT StartTime,
value,
DATEADD(SECOND, -DATEPART(SECOND, startTime), DATEADD(MINUTE, -DATEPART(MINUTE, startTime), startTime)) As startHour
FROM #T
), cte2 as
(
SELECT StartTime,
value,
StartHour,
DATEDIFF(MINUTE, ISNULL(LAG(startTime) OVER(Partition by startHour ORDER BY startTime), startHour), startTime) As minutes
FROM cte1
)
Then, select from the second cte:
SELECT startHour,
value,
CASE WHEN value = 1 THEN
minutes
ELSE
60 - minutes
END as minutes_1,
CASE WHEN value = 0 THEN
minutes
ELSE
60 - minutes
END as minutes_0
FROM cte2
Results:
startHour value minutes_1 minutes_0
20.12.2016 08:00:00 1 9 51
20.12.2016 09:00:00 0 7 53
20.12.2016 11:00:00 1 10 50
20.12.2016 11:00:00 1 20 40
20.12.2016 12:00:00 0 40 20
20.12.2016 23:00:00 1 44 16
21.12.2016 00:00:00 0 27 33
21.12.2016 01:00:00 0 55 5

Resources