SQL - placing values in date bucket - sql

Really struggling with this as a SQL newb, so i need to place values from the is_registered column into hourly buckets based on the time of day they were created. The below is a small sample
creation date
is_registered
2021-10-28 00:03:12.240
1
2021-10-28 00:09:16.221
1
2021-10-28 00:12:23.234
1
2021-10-29 00:03:19.240
1
2021-10-29 00:48:12:190
1
2021-10-29 01:09:36:129
1
2021-10-29 01:29:29:120
1
The result I would like to acheive (with the full dataset) is the following(buckets for each hour of the day
Date
Hour Bucket
Total in each bucket
2021-10-28
00:00-01:00
289
2021-10-28
01:00-02:00
876
--------
--------------
-------------
2021-10-29
00:00-01:00
190
2021-10-29
01:00-02:00
309
And so on.
Hope thats as enough information provided, any help would be greatly appreciated, thank you

Try following way
--Create table script
CREATE TABLE [dbo].[Table_4](
[creationdate] [datetime] NULL,
[isreg] [int] NULL
) ON [PRIMARY]
GO
--Sample data
insert into table_4 values (getdate(),1)
insert into table_4 values (getdate()-1,1)
go
-- query
WITH report(N) AS(
SELECT TOP(23) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM sys.columns
)
,hourly(creationdate) AS(
SELECT DATEADD(HOUR, t.N, d.creationdate)
FROM report t
CROSS JOIN(
SELECT DISTINCT DATEADD(DD, DATEDIFF(DD, 0, creationdate), 0) AS creationdate FROM table_4
) d
)
SELECT
convert(date, h.creationdate) as [Creation date],
convert(varchar(5), DATEPART(HOUR, h.creationdate)-1)+ ' - ' + convert(varchar(5),DATEPART(HOUR, h.creationdate)) as [Hour Bucket],
[Total in each bucket] = ISNULL(t.isreg, 0)
FROM hourly h
LEFT JOIN(
SELECT
creationdate = DATEADD(HOUR, DATEPART(HOUR, creationdate) ,DATEADD(DD, DATEDIFF(DD, 0, creationdate), 0)),
isreg = COUNT(*)
FROM table_4
GROUP BY DATEADD(DD, DATEDIFF(DD, 0, creationdate), 0), DATEPART(HOUR, creationdate)
)t
ON t.creationdate = h.creationdate
order by h.creationdate
Reference link

Using this tvf may be more maintainable:
DateRange TVF
WITH Ranges
AS
(
SELECT [Value] AS date_from
,LEAD([Value]) OVER (ORDER BY [Value]) AS date_to
,CAST([Value] AS date) AS [Date]
,CONVERT(char(5), [Value], 14) + '-'
+ CONVERT(char(5), LEAD([Value]) OVER (ORDER BY [Value]), 14) AS Hour_Bucket
FROM dbo.DateRange('2021-10-28', '2021-10-30', 'hh', 1)
)
SELECT R.[Date], R.Hour_Bucket
,COUNT(T.is_registered) AS Total_Registered
FROM Ranges R
LEFT JOIN YourTable T
ON T.creation_date >= R.date_from
AND T.creation_date < R.date_to
AND T.is_registered = 1
WHERE R.date_to IS NOT NULL
GROUP BY R.[Date], R.Hour_Bucket
ORDER BY [Date], Hour_Bucket;

Related

How can i split dates into 15 days intervals in a month

I have sample data set like this :
CREATE TABLE Data
(
Start DATETIME,
EndDt DATETIME,
Amount INT
)
GO
--Insert Data
INSERT INTO Data(Start,EndDt,Amount)
VALUES
('14-Apr-14','13-May-14',200),
('15-May-14','16-Jun-14',320)
Sample Input :
Start End Amount
14-Apr-14 13-May-14 200
15-May-14 16-Jun-14 320
Sample Output :
Start End Amount
14-Apr-14 30-Apr-14 100
01-May-14 13-May-14 100
15-May-14 31-May-14 160
01-Jun-14 16-Jun-14 160
can anyone suggest on this?
This solution works, but assumes you have an ID column on your table. I create a Tally table to be able to generate the date ranges (which in in sets of 16 days, not 15), and then finally get a count in the group and divide Amount by it:
CREATE TABLE dbo.[Data] (ID int IDENTITY,
[Start] date,
EndDt date,
Amount int);
GO
INSERT INTO dbo.[Data] ([Start],
EndDt,
Amount)
VALUES ('14-Apr-14', '13-May-14', 200),
('15-May-14', '16-Jun-14', 320);
GO
--Create a Tally
WITH N AS
(SELECT N
FROM (VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) N (N) ),
Tally AS
(SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1,
N N2,
N N3), --1000 rows, more than enough
--Create the dates
Dates AS(
SELECT D.ID,
DATEADD(DAY, (T.I - 1) * 16, D.[Start]) AS [Start],
DATEADD(DAY, T.I * 16, D.[Start]) AS Enddt,
D.EndDt AS FinalDate,
D.Amount
FROM dbo.[Data] D
CROSS JOIN Tally T
WHERE DATEADD(DAY, (T.I - 1) * 16, D.[Start]) < D.EndDt)
--Final result set
SELECT D.ID,
LAG(DATEADD(DAY,1,D.Enddt),1,D.[Start]) OVER (PARTITION BY D.ID ORDER BY D.[Start]) AS [Start],
CASE WHEN D.Enddt > D.FinalDate THEN D.FinalDate ELSE D.Enddt END AS EndDt,
D.Amount / COUNT(ID) OVER (PARTITION BY ID) AS Amount
FROM Dates D
ORDER BY ID, [Start];
DB<>fiddle
Try this. I have tried this code on your table and it is giving desired output
select start as dd,'start' as type,amount/2 as amount from data
union all
select enddt as dd,'end' as type,amount/2 as amount from data
select case when type = 'start' then dd--DATEADD(mm, DATEDIFF(mm, 0, dd), 0)
else DATEADD(mm, DATEDIFF(mm, 0, dd), 0) end
as start,case when type = 'start' then DATEADD (dd, -1, DATEADD(mm, DATEDIFF(mm, 0, dd) + 1, 0))
else dd end as [end],amount
from (select start as dd,'start' as type,amount/2 as amount from data
union all
select enddt as dd,'end' as type,amount/2 as amount from data) a
You can use this query:
select Start a, last_day(Start) b, amount/2 c from Data
union
select trunc(EndDt , 'MM') a, EndDt b, amount/2 c from Data;
Output

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);

Yet another LEFT OUTER JOIN fail in SQL Server 2012 Express

I'm wondering if someone might be able to troubleshoot my query. I have a simple table that has project savings per month. There are always 12 consecutive months worth or savings, but the first month can vary (e.g.: start from January for 12 months, start from March for 12 months, etc).
I need a report that gets me all savings (by month) for a given year. This means that for some project savings, if the start month is not January, then some of that project savings will fall in a different report year.
So I need a query that will return all months for the current report year, and have zero haves for where a project doesn't have saving values for that month.
I have some projects starting in July, and I'm only getting back those 6 months with their value. That is, the left join back to the date WITH is not outer joining properly. Can someone tell me where I'm going wrong please?
See code below:
DECLARE #MonthEndSnapshot SMALLDATETIME;
SELECT #MonthEndSnapshot = getdate()
DECLARE #StartDate SMALLDATETIME, #EndDate SMALLDATETIME;
SELECT #StartDate = FORMAT(#MonthEndSnapshot, 'yyyy') + '0101', #EndDate = FORMAT(#MonthEndSnapshot, 'yyyy') + '1231';
;WITH d(d) AS
(
SELECT
DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, #StartDate), 0))
FROM
(SELECT TOP
(DATEDIFF(MONTH, #StartDate, #EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM
sys.all_objects
ORDER BY [object_id]) AS n
)
select
left(datename(month, d.d), 3) as xAxisValueMon,
datepart(mm, d.d) as xAxisValue,
a.ProjectId as ProjectId,
ISNULL(SUM(a.Saving), 0) as yAxisValue
from
d
LEFT OUTER JOIN
(SELECT
mes.ProjectId, mes.Saving, mes.SavingMonth
FROM
dbo.sf_SnapshotMonthEndSaving() mes) AS a ON d.d = a.SavingMonth
group by
a.ProjectId, datename(month, d.d), datepart(mm, d.d)
order by
a.ProjectId, datepart(mm, d.d)
The WITH d(d) part works, and returns 12 month dates (1st month from Jan to Dec).
I also tried the following structure as the query:
;WITH d(d) AS
(
SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, #StartDate), 0))
FROM ( SELECT TOP (DATEDIFF(MONTH, #StartDate, #EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
select left(datename(month, d.d), 3) as xAxisValueMon,
datepart(mm, d.d) as xAxisValue,
mes.ProjectId as ProjectId,
ISNULL(SUM(mes.Saving), 0) as yAxisValue
from d LEFT OUTER JOIN
dbo.sf_SnapshotMonthEndSaving() mes
ON d.d = mes.SavingMonth
group by mes.ProjectId, datename(month, d.d), datepart(mm, d.d)
order by mes.ProjectId, datepart(mm, d.d)
But same results. The MonthEndSaving table is as follows:
CREATE TABLE [dbo].[MonthEndSaving]
(
[MonthEndSavingId] [int] IDENTITY(1,1) NOT NULL,
[MonthEndSnapshot] [datetime] NOT NULL,
[ProjectId] [int] NOT NULL,
[SavingMonth] [datetime] NOT NULL,
[Saving] [money] NOT NULL,
[DateCreated] [datetime] NOT NULL,
PRIMARY KEY CLUSTERED (MonthEndSavingId)
)
GO
ALTER TABLE MonthEndSaving
ADD CONSTRAINT [ProjectMonthEndSaving]
FOREIGN KEY (ProjectId) REFERENCES [dbo].[Project](ProjectId)
GO
Dang, Laughing Vergil seems to be a faster typist =)
Anyway, the idea is pretty much the same. Your 'error' was that you join each month to ALL the projects in dbo.sf_SnapshotMonthEndSaving(). If one fits, it gets returned for that one only, if two fit, it will show those two etc... but it will NOT repeat for EVERY project. This should.
DECLARE #StartDate datetime = '1 jan 2016',
#EndDate datetime = '1 dec 2016'
;WITH d(FirstDayOfMonth) AS
(
SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, #StartDate), 0))
FROM ( SELECT TOP (DATEDIFF(MONTH, #StartDate, #EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
),
RelevantProjects AS
(
SELECT DISTINCT ProjectId
FROM dbo.sf_SnapshotMonthEndSaving() mes
WHERE mes.SavingMonth BETWEEN #StartDate AND #EndDate -- you could also join to d but I think this is faster
),
ProjectsAndDates AS
(
SELECT ProjectID,
FirstDayOfMonth
FROM d
CROSS JOIN RelevantProjects
)
select left(datename(month, d.FirstDayOfMonth), 3) as xAxisValueMon,
datepart(mm, d.FirstDayOfMonth) as xAxisValue,
d.ProjectId as ProjectId,
ISNULL(SUM(mes.Saving), 0) as yAxisValue
from ProjectsAndDates d
LEFT OUTER JOIN [MonthEndSaving] mes -- dbo.sf_SnapshotMonthEndSaving() mes
ON mes.SavingMonth = d.FirstDayOfMonth
AND mes.Project_id = d.ProjectID
group by d.ProjectId, datename(month, d.FirstDayOfMonth), datepart(mm, d.FirstDayOfMonth)
order by d.ProjectId, datepart(mm, d.FirstDayOfMonth)
This code should do what you need:
DECLARE #MonthEndSnapshot SMALLDATETIME;
SELECT #MonthEndSnapshot = getdate()
DECLARE #StartDate SMALLDATETIME, #EndDate SMALLDATETIME;
SELECT #StartDate = FORMAT(#MonthEndSnapshot, 'yyyy') + '0101', #EndDate = FORMAT(#MonthEndSnapshot, 'yyyy') + '1231';
;WITH d(d) AS
(
SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, #StartDate), 0))
FROM ( SELECT TOP (DATEDIFF(MONTH, #StartDate, #EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
select left(datename(month, d.d), 3) as xAxisValueMon,
datepart(mm, d.d) as xAxisValue,
prj.ProjectId as ProjectId,
ISNULL(SUM(a.Saving), 0) as yAxisValue
from d
CROSS JOIN
(
SELECT DISTINCT mes.ProjectId
FROM dbo.sf_SnapshotMonthEndSaving() mes
) as prj
LEFT OUTER JOIN
(
SELECT mes.ProjectId, mes.Saving, mes.SavingMonth
FROM dbo.sf_SnapshotMonthEndSaving() mes
) as a
ON d.d = a.SavingMonth
AND prj.ProjectID = a.ProjectID
group by prj.ProjectId, datename(month, d.d), datepart(mm, d.d)
order by prj.ProjectId, datepart(mm, d.d)

Need to delete three consecutive days excluding weekends and holidays in sql

I have a requirement to delete rows from a table with three consecutive days (exclude the days if weekends come in between)
CREATE TABLE [dbo].[Test]
(
[Scanid] [bigint] NULL,
[Employeeid] [int] NULL,
[Datescanned] [datetime] NULL
)
INSERT INTO [dbo].[Test]([Scanid], [Employeeid], [Datescanned])
VALUES (108639, 3820, '2016-04-28 17:12:33.000'),
(108639, 3820, '2016-04-28 18:05:46.000'),
(108639, 3820, '2016-04-28 20:58:36.000'),
(999999, 3820, '2016-04-29 10:08:00.000'),
(999999, 3820, '2016-04-29 10:12:10.000'),
(777777, 3820, '2016-05-02 10:12:00.000'),
(111111, 3820, '2016-04-04 10:12:00.000'),
(33333, 3820, '2016-04-11 17:23:00.000'),
(987623, 3820, '2016-04-18 11:12:00.000'),
(1234, 3820, '2016-05-26 10:00:00.000'),
(5678, 3820, '2016-05-27 10:00:00.000'),
(8920, 3820, '2016-05-31 10:00:00.000')
Output:
Scanid Employeeid Datescanned
----------------------------------------
108639 3820 2016-04-28 17:12:33.000
108639 3820 2016-04-28 18:05:46.000
108639 3820 2016-04-28 20:58:36.000
999999 3820 2016-04-29 10:08:00.000
999999 3820 2016-04-29 10:12:10.000
777777 3820 2016-05-02 10:12:00.000
111111 3820 2016-04-04 10:12:00.000
33333 3820 2016-04-11 17:23:00.000
987623 3820 2016-04-18 11:12:00.000
1234 3820 2016-05-26 10:00:00.000
5678 3820 2016-05-27 10:00:00.000
8920 3820 2016-05-31 10:00:00.000
We can take date only from datescanned field and then in the above example we should delete rows with 3 consecutive date from '2016-04-28' to '2016-05-02' (2016-04-30 and 31 are weekends so we can ignore) and also delete rows with 3 consecutive date from '2016-05-26' to '2016-05-31' (2016-05-29 and 30th are weekends so we can ignore). so only results should display row with days 2016-04-04,2016-04-11,2016-04-18 which don't have 3 consecutive days before or after them.
Here is the exact output that you want..
I could see one mistake in your question,[ie. delete rows with 3 consecutive date from '2016-05-26' to '2016-05-31' (2016-05-29 and 30th are weekends so we can ignore)'],those weekends days are not correct..and the correct dates are 2016-05-28 and 2016-05-29.
DROP TABLE [TestDates]
GO
CREATE TABLE [dbo].[TestDates](
[Scanid] [bigint] NULL,
[Employeeid] [int] NULL,
[Datescanned] [datetime] NULL
)
INSERT INTO [dbo].[TestDates] ([Scanid] ,[Employeeid],[Datescanned])
VALUES (108639,3820,'2016-04-28 17:12:33.000'),(108639,3820,'2016-04-28 18:05:46.000'),
(108639,3820,'2016-04-28 20:58:36.000'),(999999,3820,'2016-04-29 10:08:00.000'),
(999999,3820,'2016-04-29 10:12:10.000'),(777777,3820,'2016-05-02 10:12:00.000'),
(111111,3820,'2016-04-04 10:12:00.000'),(33333,3820,'2016-04-11 17:23:00.000'),
(987623,3820,'2016-04-18 11:12:00.000'),(1234,3820,'2016-05-26 10:00:00.000'),
(5678,3820,'2016-05-27 10:00:00.00'), (8920, 3820, '2016-05-30 10:00:00.000')
GO
DROP TABLE #t
GO
SELECT DISTINCT Employeeid,CONVERT(date,Datescanned) Datescanned INTO #T
FROM [TestDates]
GO
;WITH cte_cnt
AS
(
SELECT Employeeid, MIN(Datescanned) AS FROM_DATE
,MAX(Datescanned) AS TO_DATE
, COUNT('A') AS DayDiff
FROM (
SELECT Employeeid,Datescanned,
ROW_NUMBER() OVER(ORDER BY Datescanned) AS ROW_NUMBER,
DATEDIFF(D, ROW_NUMBER() OVER(ORDER BY Datescanned)
,CASE WHEN DATENAME(dw, cast (Datescanned as datetime)-1) = 'Sunday' THEN DATEADD(DAY, -2, Datescanned) ELSE Datescanned END) AS Diff
FROM #t) AS dt
GROUP BY Employeeid, Diff )
DELETE t
--SELECT *
FROM cte_cnt c
JOIN [TestDates] t
ON c.Employeeid=t.Employeeid
WHERE CAST(t.Datescanned as DATE) BETWEEN c.FROM_DATE AND c.TO_DATE and c.DayDiff=3
GO
SELECT *
FROM [TestDates]
GO
A solution which doesn't account for holidays would be
SELECT
t.*
FROM (SELECT DISTINCT
CAST(t1.datescanned AS date) first_date,
CAST(t2.datescanned AS date) second_date,
CAST(t3.datescanned AS date) third_date
FROM test t1
JOIN test t2 --add a join condition for employeeid as well
ON DATEDIFF(dd, CAST(t1.datescanned AS date), CAST(t2.datescanned AS date)) = 1
OR (DATEPART(WEEKDAY, CAST(t2.datescanned AS date)) = 2
AND DATEDIFF(dd, CAST(t1.datescanned AS date), CAST(t2.datescanned AS date)) = 3)
JOIN test t3 --add a join condition for employeeid as well
ON DATEDIFF(dd, CAST(t2.datescanned AS date), CAST(t3.datescanned AS date)) = 1
OR (DATEPART(WEEKDAY, CAST(t3.datescanned AS date)) = 2
AND DATEDIFF(dd, CAST(t2.datescanned AS date), CAST(t3.datescanned AS date)) = 3)
) x
JOIN test t
ON CAST(t.datescanned AS date) = x.first_date
OR CAST(t.datescanned AS date) = x.second_date
OR CAST(t.datescanned AS date) = x.third_date
Self join the table twice, each time on
a date difference of 1 or
3 when a weekend occurs and check if the weekday is Monday (weekday=2)
Sample demo
The result gives the rows which need to be deleted. But the caveat here is that, this would give you more than 3 consecutive days if there are no gaps. In that case you need to explain if you want to stop deleting at the 3rd day.
The previous scripts are good. I would make an improvement and for weekend and Holiday check, add a function and call it in the select statement.
Here is a simple function that you can use (I am assuming that you have a table called Holiday, which holds all holiday dates per State )
Create FUNCTION [dbo].[IsHolidayOrWeekend]
(
#date DateTime,
#stateId int
)
RETURNS Bit
AS
BEGIN
declare #dayOfWeek VARCHAR(9);
set #dayOfWeek = DATEName(DW, #date);
IF(#dayOfWeek = 'Saturday' OR #dayOfWeek = 'Sunday')
RETURN 1;
ELSE
begin
set #date = cast(#date as date) -- Remove the time portion
RETURN IsNull((SELECT 1 from Holiday where StateId = #provinceId and HolidayDate = #date ), 0)
end;
END
Perhaps this?:
delete from Test
where not exists (
select 1
from Test t2
where cast(t2.Datescanned as date)
between
dateadd(day,
case datepart(dayofweek, cast(Test.Datescanned as date))
when 1 then -4 when 2 then -4
else -2
end,
cast(Test.Datescanned as date)
)
and
dateadd(day,
case datepart(dayofweek, cast(Test.Datescanned as date))
when 4 then 4 when 5 then 4
else 2
end,
cast(Test.Datescanned as date)
)
)
I have an added requirement to igonre holidays also along with weekend. I cretaed Test_calendar table removing all holidays and weekends and assigned row number to active days. Then this is the code I used. It working but may not be the fast if we have millions of rows. For me data is small so it will get the work done. Please let me know if you can simplify deletion process to make it fast.
SELECT distinct scanid as [badgeid] , Employeeid,CONVERT(date, Datescanned) as Datescanned,RN
into #test1
FROM [dbo].[Test] a
inner join Test_Calendar b
on CONVERT(date, a.Datescanned)=b.Cal_date
order by Datescanned asc
declare #min int
declare #max int
declare #i int
select #min=MIN(rn) from #test1
select #max=Max(rn) from #test1
while(#min<#max)
begin
select #i=COUNT(*) from #test1 where rn in(#min ,#min+1,#min+2)
if(#i=3)
select * from #test1 where rn in(#min ,#min+1,#min+2)
set #min=#min+1
end

How many Stored Procedures created everyday ( problem in converting Datetime )?

I make a query that return to me the count of Stored Procedure that created everyday as follow
SELECT convert(varchar, crdate, 103) as Date,Count(*) as Counter
FROM sysobjects
WHERE (xtype = 'p') AND (name NOT LIKE 'dt%')
Group by convert(varchar, crdate, 103)
and its already work but dates appear in string format that i can't order it such as below
01/03/2010 3
01/04/2008 4
01/05/2010 5
01/11/2008 1
01/12/2008 4
02/03/2008 1
02/03/2010 2
02/04/2008 4
02/05/2010 2
02/11/2008 2
02/11/2009 2
02/12/2008 4
03/01/2010 1
03/02/2010 2
03/03/2010 2
03/04/2008 2
03/04/2010 2
03/05/2008 1
03/05/2010 2
I want to make that in which date is in datetime format that i can make order by successfully, i tried convert(datetime, crdate, 103) but it show Full date
any idea of how to do ?
To get a sortable date you need year, then month, then date. use format "112".
SELECT convert(varchar, crdate, 112) as Date,Count(*) as Counter
FROM sysobjects
WHERE (xtype = 'p') AND (name NOT LIKE 'dt%')
Group by convert(varchar, crdate, 112)
order by Date
which gives this:
Date Counter
20040711 124
20040713 1
20040725 1
20040726 2
20040803 6
If you want to get the right sort order but a differently formatted date, then you can use a subquery like this.
select CONVERT(varchar, GroupDate, 103) Date, Counter
FROM (
SELECT MIN(crdate) as GroupDate,Count(*) as Counter
FROM sysobjects
WHERE (xtype = 'p') AND (name NOT LIKE 'dt%')
Group by convert(varchar, crdate, 112)
) A
order by GroupDate
which yields
Date Counter
11/07/2004 124
13/07/2004 1
25/07/2004 1
26/07/2004 2
03/08/2004 6
How about this:
select dt, COUNT(*) from
(
SELECT convert(datetime, convert(varchar, crdate, 112)) as dt
FROM sysobjects
WHERE (xtype = 'p') AND (name NOT LIKE 'dt%')
) DayOnly
group by dt
order by dt asc
SELECT DATEADD(day, DATEDIFF(day, 0, o.crdate), 0) [Date],
COUNT(*) [Counter]
FROM sys.sysobjects o
WHERE o.xtype = 'p' AND o.name NOT LIKE 'dt%'
GROUP BY DATEADD(day, DATEDIFF(day, 0, o.crdate), 0)