I have a table with a StartDate column and an EndDate column. I need to insert into a new table a row for each hour in the date range of the above tables column.
The table I have looks like this
StartDate EndDate
2017-10-25 19:00:00.000 2017-11-30 23:59:59.997
2017-10-26 13:00:00.000 2017-12-1 23:59:59.997
new table I need should look like this
Date Hour
2017-10-25 19
2017-10-25 20
2017-10-25 21
2017-10-25 22
2017-10-25 23
2017-10-26 0
2017-10-26 1
2017-10-26 2
:::::::::: :
:::::::::: :
2017-11-30 22
2017-11-30 23
I am so lost, please help!
Can be done with an ad-hoc tally table in concert with a CROSS APPLY.
Example
Select Date = cast(D as date)
,Hour = datepart(HOUR,D)
From YourTable A
Cross Apply (
Select Top (DateDiff(HOUR,A.StartDate,A.EndDate)+1) D=DateAdd(HOUR,-1+Row_Number() Over (Order By (Select Null)),A.StartDate)
From master..spt_values n1,master..spt_values n2
) B
Returns
Date Hour
2017-10-25 19
2017-10-25 20
2017-10-25 21
2017-10-25 22
2017-10-25 23
2017-10-26 0
2017-10-26 1
2017-10-26 2
2017-10-26 3
...
Related
I have a calendar table where working days are marked.
Now I need a running total called "current_working_day" which sums up the working days until the end of a month and restarts again.
This is my query:
select
WDAYS.Date,
WDAYS.DayName,
WDAYS.WorkingDay,
sum(WDAYS.WorkingDay) OVER(order by (Date), MONTH(Date), YEAR(Date)) as 'current_working_day',
sum(WDAYS.WorkingDay) OVER(PARTITION by YEAR(WDAYS.Date), MONTH(WDAYS.Date) ) total_working_days_per_month
from WDAYS
where YEAR(WDAYS.Date) = 2022
This is my current output
Date
DayName
WorkingDay
current_working_day
total_working_days_per_month
2022-01-27
Thursday
1
19
21
2022-01-28
Friday
1
20
21
2022-01-29
Saturday
0
20
21
2022-01-30
Sunday
0
20
21
2022-01-31
Monday
1
21
21
2022-02-01
Tuesday
1
22
20
2022-02-02
Wednesday
1
23
20
2022-02-03
Thursday
1
24
20
But the column "current_workind_day" should be like this
Date
DayName
WorkingDay
current_working_day
total_working_days_per_month
2022-01-27
Thursday
1
19
21
2022-01-28
Friday
1
20
21
2022-01-29
Saturday
0
20
21
2022-01-30
Sunday
0
20
21
2022-01-31
Monday
1
21
21
2022-02-01
Tuesday
1
1
20
2022-02-02
Wednesday
1
2
20
2022-02-03
Thursday
1
3
20
Thanks for any advice.
You can try to use PARTITION by with EOMONTH function which might get the same result but better performance, then you might only need to order by Date instead of using the function with the date.
select
WDAYS.Date,
WDAYS.DayName,
WDAYS.WorkingDay,
sum(WDAYS.WorkingDay) OVER(PARTITION by EOMONTH(WDAYS.Date) order by Date) as 'current_working_day',
sum(WDAYS.WorkingDay) OVER(PARTITION by EOMONTH(WDAYS.Date) ) total_working_days_per_month
from WDAYS
where YEAR(WDAYS.Date) = 2022
I want to create a trigger after insert and update which calculate the sum of values in multiple rows and set the sum in [Target_Cummlative]. I try the following code but that causes this error:
Msg 156, Level 15, State 1, Procedure target_cummlative, Line 13
Incorrect syntax near the keyword 'FROM
My code:
CREATE TRIGGER [dbo].[target_cummlative]
ON [dbo].[Appointments]
AFTER insert, UPDATE
AS
BEGIN
IF TRIGGER_NESTLEVEL() > 1
RETURN
UPDATE T1
SET t1.[Target_Cummlative] = SUM(ISNULL(CAST([TARGET] AS FLOAT), 0)
FROM [Appointments] T1
INNER JOIN inserted i ON T1.[UniqueId] = i.[UniqueId]
GROUP BY CONVERT(VARCHAR(10), t1.[StartDate], 111), t1.[ResourceId]
END
Here is what i have currently, as you can see Target cummlative is null
Id StartDate Location ResourceId TARGET Target_Cummlative
1381 2019-07-22 07:00:00 41051 1 20 NULL
1382 2019-07-22 08:00:00 41051 1 20 NULL
1383 2019-07-22 09:15:00 41051 1 15 NULL
1384 2019-07-22 10:00:00 41051 1 20 NULL
1385 2019-07-22 11:00:00 41051 1 20 NULL
1386 2019-07-22 12:30:00 41051 1 8 NULL
I want set the sum the values in TARGET column and update Target cummlative as
Id StartDate Location ResourceId TARGET Target_Cummlative
1381 2019-07-22 07:00:00 41051 1 20 103
1382 2019-07-22 08:00:00 41051 1 20 103
1383 2019-07-22 09:15:00 41051 1 15 103
1384 2019-07-22 10:00:00 41051 1 20 103
1385 2019-07-22 11:00:00 41051 1 20 103
1386 2019-07-22 12:30:00 41051 1 8 103
If possible - I'd advice against storing cumulative data in the same table, if possible - better create a separate one to store aggregated information.
In any case - your issue is missing parenthesis in SET clause.
CREATE TRIGGER [dbo].[target_cumulative]
ON [dbo].[Appointments]
AFTER insert, UPDATE
AS
BEGIN
IF TRIGGER_NESTLEVEL() > 1
RETURN
UPDATE T1
SET T1.[Target_Cumulative] = SUM(ISNULL(CAST([TARGET] AS FLOAT), 0))
FROM dbo.Appointments AS T1
INNER JOIN inserted AS ION I.UniqueId = T1.UniqueId
GROUP BY CONVERT(DATE, T1.StartDate), T1.ResourceId;
END;
Oh - and there's typo in cummlative, it should be cumulative
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)
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
This question already has answers here:
Sql Date Grouping with avaliable dates in database
(3 answers)
Closed 4 years ago.
ID DateTime EmailCount
93 6/1/2014 00:00:00 4
94 6/2/2014 00:00:00 4
95 6/3/2014 00:00:00 2
96 6/4/2014 00:00:00 2
97 6/5/2014 00:00:00 2
98 6/6/2014 00:00:00 2
99 6/7/2014 00:00:00 2
73 6/8/2014 00:00:00 2
74 6/9/2014 00:00:00 2
75 6/10/2014 00:00:00 4
76 6/11/2014 00:00:00 4
77 6/12/2014 00:00:00 2
78 6/13/2014 00:00:00 2
79 6/14/2014 00:00:00 2
80 6/16/2014 00:00:00 2
81 6/17/2014 00:00:00 4
82 6/18/2014 00:00:00 4
83 6/19/2014 00:00:00 4
84 6/20/2014 00:00:00 4
100 6/21/2014 00:00:00 4
101 6/22/2014 00:00:00 4
102 6/23/2014 00:00:00 4
103 6/24/2014 00:00:00 4
89 6/27/2014 00:00:00 4
90 6/28/2014 00:00:00 4
91 6/29/2014 00:00:00 4
92 6/30/2014 00:00:00 4
104 7/1/2014 00:00:00 4
105 7/2/2014 00:00:00 4
106 7/3/2014 00:00:00 4
121 7/6/2014 00:00:00 2
122 7/7/2014 00:00:00 2
123 7/8/2014 00:00:00 2
Generated Output
Startdate EndDate EmailCount
6/3/2014 00:00:00 6/14/2014 00:00:00 2
6/16/2014 00:00:00 6/16/2014 00:00:00 2
7/6/2014 00:00:00 7/8/2014 00:00:00 2
6/1/2014 00:00:00 6/11/2014 00:00:00 4
6/17/2014 00:00:00 6/24/2014 00:00:00 4
6/27/2014 00:00:00 7/3/2014 00:00:00 4
Here, the generated output is not perfect because I want StartDate to EndDate in groups like: (6/3/2014 to 6/9/2014 and EmailCount = 2) and (6/10/2014 to 6/11/2014 and EmailCount =4) and (6/12/2014 to 6/14/2014 and EmailCount =2). Also, date not in database should not be added to group.
A somewhat complex query to explain, but here goes an attempt;
If the time is always midnight, you could use a common table expression to assign a row number to each row, and group by the difference between the date and row number. As long as the sequence is not broken (ie the dates are consecutive and with the same emailid) they will end up in the same group and an outer query can easily extract the start and end date for each group;
WITH cte AS (
SELECT dateandtime, emailid,
ROW_NUMBER() OVER (PARTITION BY emailid ORDER BY dateandtime) rn
FROM mytable
)
SELECT MIN(dateandtime) start_time,
MAX(dateandtime) end_time,
MAX(emailid) emailid
FROM cte GROUP BY DATEADD(d, -rn, dateandtime) ORDER BY start_time
An SQLfiddle to test with.
If the datetimes are not always midnight, the grouping will fail. If that's the case, you could add a common table expression that converts the datetime to a date as a separate step before running this query.
You're looking for runs of consecutive dates in blocks with the same EmailID. This assumes you have no gaps in the dates. I'm not sure it's the most elegant approach but you can find a lot of stuff on this topic.
with BlockStart as (
select t.StartDate, t.EmailID
from T as t left outer join T as t2
on t2.StartDate = t1.StartDate - 1 and t2.EmailID = t1.EmailID
where t2.StartDate is null
union all
select max(StartDate) + 1, null
from T
) as BlockStart
select
StartDate,
(select min(StartDate) - 1 from BlockStart as bs2 where bs2 > bs.StartDate) as EndDate,
EmailID
from BlockStart as bs
where
EmailID is not null
-- /* or */ exists (select 1 from BlockStart as bs3 where bs3.StartDate > bs.StartDate)