SQL Select birthdays in specific date range - sql

I have following:
DECLARE #TempTable TABLE
(
[Id] [int],
[FirstName] [varchar](40) NULL,
[Birthdate] [date] NULL
);
insert into #TempTable values (1, 'A', convert(date, '05/25/2017', 101))
insert into #TempTable values (2, 'B', convert(date, '06/25/2017', 101))
What I need is query which will return all Birthdays in range from StartDate to to EndDate.
To be more here is what I expect to get as a result:
Case 1:
If date range is set to:
DECLARE #StartDate datetime = '05/01/2017';
DECLARE #EndDate datetime = '07/01/2017';
Query should return:
1 A 2017-05-25
2 B 2017-06-25
Case 2:
If date range is set to:
DECLARE #StartDate datetime = '05/01/2017';
DECLARE #EndDate datetime = '06/01/2017';
Query should return:
1 A 2017-05-25
Case 3:
If date range is set to:
DECLARE #StartDate datetime = '05/01/2015';
DECLARE #EndDate datetime = '07/01/2017';
Query should return:
1 A 2017-05-25
1 A 2016-05-25
1 A 2015-05-25
2 B 2017-06-25
2 B 2016-06-25
2 B 2015-06-25
Case 4:
If date range is set to:
DECLARE #StartDate datetime = '05/01/2015';
DECLARE #EndDate datetime = '06/01/2017';
Query should return
1 A 2017-05-25
1 A 2016-05-25
1 A 2015-05-25
2 B 2016-06-25
2 B 2015-06-25

Firstly we creates all dates for given range . then apply logic
DECLARE #StartDate datetime = '05/01/2015';
DECLARE #EndDate datetime = '06/01/2017';
;With DateSequence as
(
Select #StartDate as Bdate
union all
Select dateadd(day, 1, Bdate)
from DateSequence
where Bdate < #EndDate
)
Select ID,FirstName,Bdate as BirthDate from DateSequence
cross join #TempTable
where Bdate between #StartDate and #EndDate and
month(Bdate)= month(BirthDate) and day(Bdate) = day(BirthDate)
order by ID asc , Bdate desc
option (MaxRecursion 2000)
OutPut :

At its most basic level, you can do this by creating a list of everyone's birthday for 100 years, then filtering...
with numbers as
(
select 0 as NN
union all
select NN+1
from numbers
where NN < 100
)
select id, dateadd(yy,NN,BirthDate) as Birthdays
from numbers
cross join #TempTable
where dateadd(yy,NN,BirthDate) between #StartDate and #EndDate

Related

SQL all records that covers to dates in SQL server 2016

I have a table as the below:(date format is dd/MM/yyyy) (SQL server 2016)
ID FromDate ToDate
1 01/01/2018 30/01/2018
2 02/02/2018 02/03/2018
3 03/03/2018 03/04/2018
4 04/04/2018 05/05/2018
I need to get the records that covers all the period I supply( as from-to dates).
For example if I supplied the dates (StartDate=10/02/2018) and (EndDate=01/05/2018) the query should return the records 2,3,4
since the startDate is within the record of ID=2 and the endDate within the record of ID=4 and the rest of the dates is between the records 2,3,4/.
If there is a break in the dates then it should return nothing,for example if I supplied the dates (StartDate=01/02/2018) and (EndDate=01/05/2018)
nothing should be returned since 01/02/2018 is not covered by any dates.
Also as an another example:
ID FromDate ToDate
1 01/01/2018 30/01/2018
2 02/02/2018 02/03/2018
3 06/03/2018 03/04/2018
4 04/04/2018 05/05/2018
if I supplied (StartDate=10/02/2018) and (EndDate=01/05/2018)
should return nothing, since there is a break of dates between 02/03/2018 and 06/03/2018
Edit: for date breaks
declare #t table
(id int identity,
FromDate date,
ToDate date
);
insert into #t(FromDate, ToDate)
values
('20180101', '20180201'),
('20180202', '20180302'),
('20180303', '20180403'),
('20180404', '20180505');
declare #fromdate date = '20180210',
#todate date = '20180501';
select *
from #t
where FromDate <= #todate
and Todate >= #fromdate;
with d
as
(
select *, lag(ToDate) over(order by FromDate) as PreviousToDate
from #t
where FromDate <= #todate
and Todate >= #fromdate
)
select Id, FromDate, ToDate
from d
where not exists(select * from d as bd where bd.FromDate<>dateadd(day, 1, bd.PreviousToDate));
create table t
(id int identity,
FromDate date,
ToDate date
)
insert into t(FromDate, ToDate)
values
('20180101', '20180201'),
('20180202', '20180302'),
('20180303', '20180403'),
('20180404', '20180505')
declare #fromdate date = '20180210',
#todate date = '20180501';
declare #fromdatetopick date,#todatetopick date,#idtopick int,#count int,#fromdatepick date,#todatepick date,#idpick int,#datediff int,#maxdatediff int
set #count=0
set #maxdatediff=0
declare abc cursor for
select *
from t
where FromDate >= #fromdate
or (Todate <= #todate and Todate>=#fromdate)
open abc
fetch next from abc into #idtopick,#fromdatetopick,#todatetopick
while ##FETCH_STATUS=0
begin
if (#count=0)
begin
set #todatepick=#todatetopick
end
else
begin
set #fromdatepick=#fromdatetopick
set #datediff=DATEDIFF(day,#todatepick,#fromdatepick)
if (#datediff>#maxdatediff)
begin
set #maxdatediff=#datediff
end
end
set #todatepick=#todatetopick
set #count=#count+1
fetch next from abc into #idtopick,#fromdatetopick,#todatetopick
end
close abc
deallocate abc
if (#maxdatediff>1)
begin
select * from t where id=null
end
else
begin
select *
from t
where FromDate >= #fromdate
or (Todate <= #todate and Todate>=#fromdate)
end
You would seem to want:
select t.*
from (select t.*,
lag(todate) over (order by fromdate) as prev_todate
from t
) t
where fromdate < #todate and
todate > #fromdate and
(prev_todate is null and fromdate = #fromdate or
prev_todate = dateadd(day, -1, fromdate)
);
Note that this handles the situation where the dates start on your desired fromdate.

Counting number using numbers table with 15 minutes interval

What is the most efficient way to count the number of occurrences according?
I found the numbers table is the most efficient way to generate time sequence data based on start time and end time.
I have create a number table starts from 0 to 100,000.
I have generate time sequence table as following:
Declare #Start datetime = '2018-01-01 00:00:00.000',#End datetime ='2018-02-01 00:00:00.000';
SELECT
DATEADD(MINUTE,Number*15,#Start) StartTime,
[Number],
DATEADD(MINUTE,(Number+1)*15,#Start) EndTime
FROM dbo.Numbers
Where (Number+1)*15<=DATEDIFF(MINUTE,#Start,#End)
Order By Number;
I have table of data like:
Time ID
2018-01-01 00:00:01.000 1
2018-01-01 00:00:02.000 1
2018-01-01 00:15:00.000 124
2018-01-01 00:28:00.000 341
2018-01-01 00:26:00.000 111
2018-01-01 01:02:00.000 162
2018-01-01 04:09:00.000 110
2018-01-01 05:09:00.152 398
2018-01-01 08:12:00.000 902
2018-01-01 12:45:00.000 009
2018-01-01 13:23:00.000 000
2018-01-01 15:37:00.000 187
How can I count time based on 15 minutes interval?
You can try to use cte recursive to make a calendar table then do outer join
Declare #Start datetime = '2018-01-01 00:00:00.000',#End datetime ='2018-02-01 00:00:00.000';
;WITH CTE AS (
SELECT #Start startTime,DATEADD(MINUTE,15,#Start) nextTime,#End endTime
UNION ALL
SELECT DATEADD(MINUTE,15,startTime),DATEADD(MINUTE,15,nextTime) nextTime,#End
FROM CTE
WHERE DATEADD(MINUTE,15,startTime) < #End
)
SELECT startTime,nextTime,COUNT(t1.ID)
FROM CTE c1
LEFT JOIN T t1 on t1.Time BETWEEN c1.startTime and c1.nextTime
GROUP BY startTime,nextTime
option ( MaxRecursion 0 );
Note
The CTE default maximum recursion is 100, you can sett option ( MaxRecursion 0 );
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
Sqlfiddle
How about this. Just a normal group by so you can put in HAVING, SUM, AVERAGE etc too if you want. Run top section once:
create table TestTable
(
Time datetime,
ID int
)
GO
insert into TestTable values('2018-01-01 00:00:01.000' , 1)
insert into TestTable values('2018-01-01 00:00:02.000' , 1)
insert into TestTable values('2018-01-01 00:15:00.000' ,124)
insert into TestTable values('2018-01-01 00:28:00.000' ,341)
insert into TestTable values('2018-01-01 00:26:00.000' ,111)
insert into TestTable values('2018-01-01 01:02:00.000' ,162)
GO
CREATE FUNCTION [dbo].[RoundTime] (#Time datetime, #RoundTo float) RETURNS datetime
AS
BEGIN
DECLARE #RoundedTime smalldatetime, #Multiplier float
SET #Multiplier = 24.0 / #RoundTo
SET #RoundedTime= ROUND(CAST(CAST(CONVERT(varchar, #Time, 121) AS datetime) AS float) * #Multiplier, 0) / #Multiplier
RETURN #RoundedTime
END
GO
Then the actual working:
DECLARE #startDate DATETime
DECLARE #endDate DATETime
SET #startDate = '2018-01-01'
SET #endDate = GETDATE()
DECLARE #dateAxis TABLE
(
dt DATETime
)
DECLARE #currentDate DATETime = #startDate
WHILE #currentDate <= #endDate
BEGIN
INSERT INTO #dateAxis
SELECT #currentDate
SET #currentDate = DATEADD(Minute, 15, #currentDate)
END
-- axis table
--select * from #dateAxis
SELECT
dt AS joinDt,
dataset.MyCount
FROM
#dateAxis axis
LEFT JOIN
(
SELECT
dbo.RoundTime([Time], 0.5) AS joinDt,
count(*) AS MyCount
FROM
/*Your table here*/
TestTable tt
group by
dbo.RoundTime([Time], 0.5)
) dataset
ON dataset.joinDt = axis.dt
ORDER BY
axis.dt

Insert a series of dates into sql startdate and enddate inputs

I have this table:
**ID StartDate EndDate**
1 01/01/2012 03/01/2012
2 28/09/2013 02/10/2013
3 12/06/2011 15/06/2011
And I need to have this table:
Date
**ID Date**
1 01/01/2012
1 02/01/2012
1 03/01/2012
2 28/09/2013
2 29/09/2013
2 30/09/2013
2 01/10/2013
2 02/10/2013
3 12/06/2011
3 13/06/2011
3 14/06/2011
3 15/06/2011
I Have Next Sql Code That Retarn The Dates Between The StartDate & The EndDate + StartDate +EndDate :
declare #Start datetime
declare #end datetime
declare #request int
set #Start = '2014-09-28 06:53:04.560'
set #end = '2014-09-29 11:53:04.560'
set #request = 1
;with Dates as (
select #request as reqId,#Start as reqDate
union all
select reqId+1,DATEADD(hh,1,reqDate) from Dates
where reqDate < #end
)
select * from Dates
How Can I Get This Result For A Bulk Of StartDate-EndDate Input?
You can do this by using your source date table as below
declare #request int
set #request = 1
;with Dates as (
SELECT #request as reqId,StartDate as reqDate, EndDate
FROM yourDateTable
UNION ALL
SELECT reqId+1,DATEADD(DAY,1,reqDate),Dates.EndDate
FROM Dates
WHERE DATEADD(DAY,1,reqDate) < EndDate
)
SELECT *
FROM Dates
Use recursive query
CREATE TABLE #ranges
(
Id INT ,
startDate DATE ,
ENdDate DATE
)
INSERT #ranges
( Id, startDate, ENdDate )
VALUES ( 1, '2014-5-2', '2014-5-5' ),
( 2, '2014-8-29', '2014-9-3' ),
( 3, '2014-10-2', '2014-10-8' );
WITH
cte
AS ( SELECT *
FROM #ranges
UNION ALL
SELECT id ,
DATEADD(DAY, 1, startDate) ,
EndDate
FROM cte
WHERE DATEADD(DAY, 1, startDate) <= EndDate
)
SELECT Id ,
startDate Date
FROM cte
ORDER BY Id ,
startDate
DROP TABLE #ranges
DECLARE #MyPeriod TABLE (
ID INT NOT NULL IDENTITY PRIMARY KEY
,StartDate date
,EndDate date
);
INSERT INTO #MyPeriod (StartDate, EndDate)
VALUES ('20120101','20120103')
,('20130928','20131002')
,('20110612','20110615');
SELECT *
FROM #MyPeriod
declare #Start date
declare #end date
set #Start = '2000-01-01'
set #end = '2020-01-01'
;with Dates as (
select #Start as Date
union all
select DATEADD(day,1,Date) from Dates
where Date < #end
)
select MyPeriod.ID, Dates.Date
from Dates
INNER JOIN #MyPeriod MyPeriod
ON Dates.Date BETWEEN MyPeriod.StartDate AND MyPeriod.EndDate
ORDER BY Dates.Date
OPTION(MAXRECURSION 0);
Try this!
CREATE TABLE #TempTable(
ID int,
[Date] date
)
DECLARE #ROWCOUNT INT,#ID INT
DECLARE #Date1 DATE, #Date2 DATE
SET #ROWCOUNT = 1
SET #ID=''
SET #Date1=''
SET #Date1=''
WHILE (#ROWCOUNT <= (SELECT COUNT(*) FROM your_table_name))
BEGIN
SET #ID=(SELECT id FROM your_table_name WHERE ID=#ROWCOUNT)
SET #Date1=(SELECT StartDate FROM your_table_name WHERE ID=#ROWCOUNT)
SET #Date2=(SELECT EndDate FROM your_table_name WHERE ID=#ROWCOUNT)
INSERT INTO #TempTable
SELECT #ID, DATEADD(DAY,number,#Date1) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number, #Date1) <= #Date2
SET #ROWCOUNT = #ROWCOUNT + 1
END
SELECT * FROM #TempTable
DROP TABLE #TempTable

fetching null value with specify condition with left join

i have the following data in table leave:-
EMPNO NAME DATEFROM DATETO
111 xxx 2014-08-03 00:00:00.000 2014-09-05 00:00:00.000
222 yyy 2014-10-03 00:00:00.000 2014-10-05 00:00:00.000
but when i write below query :
select empno,name,sum(datediff(day,DATEFROM,DATETO)+1) as holiday
from leave
where DATEFROM>='2014-08-01'
and DATETO<='2014-08-31'
and empno=111
group by name ,empno
then I didnt get any value..but with this query i want below output:
EMPNO NAME holiday
111 xxx 29
august has 31 days..
then how can i get?
it will only count the day of august(08) month
In your query you are specifying DateTo MUST be <= 2014-08-31... but the DateTo value on your record is 2014-09-05. So that is excluding it.
I'm assuming you want all records that start in August, so you want DateFrom in both conditions.
Per your updates/comments:
declare #data table (empno int, name varchar(5), datefrom datetime, dateto datetime)
insert into #data
select 111,'xxx','2014-08-03 00:00:00.000','2014-09-05 00:00:00.000'
declare #maxdate datetime, #mindate datetime
set #mindate = '2014-08-01'
set #maxdate = '2014-08-31'
select empno,name,sum(datediff(day,DATEFROM,case when dateto > #maxdate then #maxdate else dateto end)+1) as holiday
from #data
where DATEFROM >=#mindate and DATEFROM <=#maxdate and empno=111 group by name ,empno
Per your last question regarding weekends. What you need to do is cross join a calendar table to the event, and omit the days you don't want. From there, instead of a datediff() you can simply count the remaining days.
declare #data table (empno int, name varchar(5), datefrom datetime, dateto datetime)
insert into #data
select 111,'xxx','2014-08-03 00:00:00.000','2014-09-05 00:00:00.000'
declare #maxdate datetime, #mindate datetime
set #mindate = '2014-08-01'
set #maxdate = '2014-08-31'
;with cal as
(
select #mindate as dDate, datename(dw, #mindate) as DayWeek
union all
select dDate+1, datename(dw, dDate+1) as DayWeek
from cal
where dDate+1 <= #maxdate
)
select empno, name, count(*) as holiday
from #data d
cross join cal c
where c.ddate between d.datefrom and d.dateto
and c.dayweek not in ('saturday','sunday')
and DATEFROM >=#mindate and DATEFROM <=#maxdate and empno=111
group by name ,empno

Easiest way to populate a temp table with dates between and including 2 date parameters

What is the easiest way to populate a temp table with dates including and between 2 date parameters. I only need the 1st day of the month dates.
So for example if #StartDate = '2011-01-01' and #EndDate = '2011-08-01'
Then I want this returned in the table
2011-01-01
2011-02-01
2011-03-01
2011-04-01
2011-05-01
2011-06-01
2011-07-01
2011-08-01
This works even if the #StartDate is not the first of the month. I'm assuming that if it's not the start of the month, you want to begin with the first of the next month. Otherwise remove the +1.:
;WITH cte AS (
SELECT CASE WHEN DATEPART(Day,#StartDate) = 1 THEN #StartDate
ELSE DATEADD(Month,DATEDIFF(Month,0,#StartDate)+1,0) END AS myDate
UNION ALL
SELECT DATEADD(Month,1,myDate)
FROM cte
WHERE DATEADD(Month,1,myDate) <= #EndDate
)
SELECT myDate
FROM cte
OPTION (MAXRECURSION 0)
declare #StartDate date = '2014-01-01';
declare #EndDate date = '2014-05-05';
;WITH cte AS (
SELECT #StartDate AS myDate
UNION ALL
SELECT DATEADD(day,1,myDate) as myDate
FROM cte
WHERE DATEADD(day,1,myDate) <= #EndDate
)
SELECT myDate
FROM cte
OPTION (MAXRECURSION 0)
declare #StartDate datetime
declare #EndDate datetime
select #StartDate = '2011-01-01' , #EndDate = '2011-08-01'
select #StartDate= #StartDate-(DATEPART(DD,#StartDate)-1)
declare #temp table
(
TheDate datetime
)
while (#StartDate<=#EndDate)
begin
insert into #temp
values (#StartDate )
select #StartDate=DATEADD(MM,1,#StartDate)
end
select * from #temp
Works even if the #StartDate is not the first day of the month by going back to the initial day of the month of StartDate
this is tested in SQL 2008 R2
Declare #StartDate datetime = '2015-03-01'
Declare #EndDate datetime = '2015-03-31'
declare #temp Table
(
DayDate datetime
);
WHILE #StartDate <= #EndDate
begin
INSERT INTO #temp (DayDate) VALUES (#StartDate);
SET #StartDate = Dateadd(Day,1, #StartDate);
end ;
select * from #temp
Result:
DayDate
-----------------------
2015-03-01 00:00:00.000
2015-03-02 00:00:00.000
2015-03-03 00:00:00.000
2015-03-04 00:00:00.000
...
Interestingly, it is faster to create from enumerated data as per this article.
DECLARE #StartDate DATE = '10001201';
DECLARE #EndDate DATE = '20000101';
DECLARE #dim TABLE ([date] DATE)
INSERT #dim([date])
SELECT d
FROM
(
SELECT
d = DATEADD(DAY, rn - 1, #StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, #StartDate, #EndDate))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM
sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
ORDER BY
s1.[object_id]
) AS x
) AS y;
On my machine, it's around 60% faster with large date ranges. The recursion method can populate 2000 years worth of data in around 3 seconds though, and looks a lot nicer, so I don't really recommend this method just for incrementing days.
Correction for null dates:
IF OBJECT_ID('tempdb..#dim') IS NOT NULL
DROP TABLE #dim
CREATE TABLE #dim ([date] DATE)
if not #Begin_Date is null and not #End_Date is null
begin
INSERT #dim([date])
SELECT d
FROM(
SELECT
d = DATEADD(DAY, rn - 1, #Begin_Date)
FROM
(
SELECT TOP (DATEDIFF(DAY, #Begin_Date, #End_Date))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM
sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
ORDER BY
s1.[object_id]
) AS x
) AS y;
end
CREATE TABLE #t (d DATE)
INSERT INTO #t SELECT GETDATE()
GO
INSERT #t SELECT DATEADD(DAY, -1, MIN(d)) FROM #t
GO 10