How to Select continuous date in sql - sql

Is there any function to check for continuous date. I'm having problem on working with this issue below:
My table has a datetime column with the following data:
----------
2015-03-11
2015-03-12
2015-03-13
2015-03-16
Given start date as 2015-3-11 and end date as 2015-3-17. I want the result as:
----------
2015-03-11
2015-03-12
2015-03-13
Can anyone suggest anything ?

I'm thinking this is somewhat a variation of Grouping Islands of Contiguous Dates problem. This can be done using ROW_NUMBER():
SQL Fiddle
CREATE TABLE Test(
tDate DATETIME
)
INSERT INTO Test VALUES
('20150311'), ('20150312'), ('20150313'), ('20150316');
DECLARE #startDate DATE = '20150311'
DECLARE #endDate DATE = '20150317'
;WITH Cte AS(
SELECT
*,
RN = DATEADD(DD, - (ROW_NUMBER() OVER(ORDER BY tDATE) - 1), tDate)
FROM Test
WHERE
tDate >= #startDate
AND tDate < DATEADD(DAY, 1, #endDate)
)
SELECT CAST(tDate AS DATE)
FROM CTE
WHERE RN = #startDate
RESULT
|------------|
| 2015-03-11 |
| 2015-03-12 |
| 2015-03-13 |
Here is the SQL Server 2005 version:
SQL Fiddle
DECLARE #startDate DATETIME
DECLARE #endDate DATETIME
SET #startDate = '20150311'
SET #endDate = '20150317'
;WITH Cte AS(
SELECT
*,
RN = DATEADD(DD, -(ROW_NUMBER() OVER(ORDER BY tDATE)-1), tDate)
FROM Test
WHERE
tDate >= #startDate
AND tDate < DATEADD(DAY, 1, #endDate)
)
SELECT CONVERT(VARCHAR(10), tDate, 121)
FROM CTE
WHERE RN = #startDate

For MSSQL 2012. This will return MAX continuous groups:
DECLARE #t TABLE(d DATE)
INSERT INTO #t VALUES
('20150311'),
('20150312'),
('20150313'),
('20150316')
;WITH
c1 AS(SELECT d, IIF(DATEDIFF(dd,LAG(d, 1, DATEADD(dd, -1, d)) OVER(ORDER BY d), d) = 1, 0, 1) AS n FROM #t),
c2 AS(SELECT d, SUM(n) OVER(ORDER BY d) AS n FROM c1)
SELECT TOP 1 WITH TIES MIN(d) AS StartDate, MAX(d) AS EndDate, COUNT(*) AS DayCount
FROM c2
GROUP BY n
ORDER BY DayCount desc
Output:
StartDate EndDate DayCount
2015-03-11 2015-03-13 3
For
('20150311'),
('20150312'),
('20150313'),
('20150316'),
('20150317'),
('20150318'),
('20150319'),
('20150320')
Output:
StartDate EndDate DayCount
2015-03-16 2015-03-20 5
Apply filtering in c1 CTE:
c1 AS(SELECT d, IIF(DATEDIFF(dd,LAG(d, 1, DATEADD(dd, -1, d)) OVER(ORDER BY d), d) = 1, 0, 1) AS n FROM #t WHERE d BETWEEN '20150311' AND '20150320'),
For MSSQL 2008:
;WITH
c1 AS(SELECT d, (SELECT MAX(d) FROM #t it WHERE it.d < ot.d) AS pd FROM #t ot),
c2 AS(SELECT d, CASE WHEN DATEDIFF(dd,ISNULL(pd, DATEADD(dd, -1, d)), d) = 1 THEN 0 ELSE 1 END AS n FROM c1),
c3 AS(SELECT d, (SELECT SUM(n) FROM c2 ci WHERE ci.d <= co.d) AS n FROM c2 co)
SELECT TOP 1 WITH TIES MIN(d) AS StartDate, MAX(d) AS EndDate, COUNT(*) AS DayCount
FROM c3
GROUP BY n
ORDER BY DayCount desc

you don't need to declare any start date or end date as other answers says, you need a row_num with datediff function:
create table DateFragTest (cDate date);
insert into DateFragTest
values ('2015-3-11'),
('2015-3-12'),
('2015-3-13'),
('2015-3-16')
with cte as
(select
cDate,
row_number() over (order by cDate ) as rn
from
DateFragTest)
select cDate
from cte t1
where datediff(day,
(select cDate from cte t2 where t2.rn=t1.rn+1),
t1.cDate)<>1
Output:
cDate
2015-03-11
2015-03-12
2015-03-13
SQLFIDDLE DEMO

For sql server 2012-
WITH cte
AS
(
SELECT [datex]
, lead([datex]) OVER ( ORDER BY [datex]) lead_datex
, datediff(dd,[datex],lead([datex]) OVER ( ORDER BY [datex]) ) AS diff
FROM [dbo].[datex]
)
SELECT c.[datex]
FROM [cte] AS c
WHERE diff >=1

Use BETWEEN
The query will go like this:
SELECT *
FROM your_table_name
WHERE your_date_column_name BETWEEN '2015-3-11' AND '2015-3-13'

(dt between x and y) or just (dt >= x and dt <= y).

Related

Keep last n business days records from today date in SQL Server

How can we keep last n business days records from today date in this table:
Suppose n = 7
Sample Data:
Table1:
Date
----------
2021-11-29
2021-11-30
2021-12-01
2021-12-02
2021-12-03
2021-12-04
2021-12-05
2021-12-06
2021-12-07
2021-12-08
2021-12-09
2021-12-10
2021-12-11
2021-12-12
2021-12-13
Based on this table data we want output like below. It should delete all the rows before the 03-Dec or data for last 7 business days.
Date
-------
2021-12-03
2021-12-06
2021-12-07
2021-12-08
2021-12-09
2021-12-10
2021-12-13
Note: It's fine if we keep data for Saturday, Sunday in between business days.
I tried this query
DECLARE #n INT = 7
SELECT * FROM Table1
WHERE [date] < Dateadd(day, -((#n + (#n / 5) * 2)), Getdate())
but Saturday, Sunday logic doesn't fit here with my logic. Please suggest better approach.
You can get the 7th working day from today as
select top(1) cast(dateadd(d, -n + 1, getdate()) as date) d
from (
select n
, sum (case when datename(dw, dateadd(d, -n + 1, getdate())) not in ('Sunday', 'Saturday') then 1 end) over(order by n) wdn
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11)
)t0(n)
) t
where wdn = 7
order by n;
Generally using on-the-fly tally for a #n -th day
declare #n int = 24;
with t0(n) as (
select n
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
) t(n)
), tally as (
select top(#n + (#n/5 +1)*2) row_number() over(order by t1.n) n
from t0 t1, t0 t2, t0 t3
)
select top(1) cast(dateadd(d, -n + 1, getdate()) as date) d
from (
select n
, sum (case when datename(dw, dateadd(d, -n + 1, getdate())) not in ('Sunday', 'Saturday') then 1 end) over(order by n) wdn
from tally
) t
where wdn = #n
order by n;
You can use CTE to mark target dates and then delete all the others from the table as follows:
; With CTE As (
Select [Date], Row_number() Over (Order by [Date] Desc) As Num
From tbl
Where DATEPART(weekday, [Date]) Not In (6,7)
)
Delete From tbl
Where [Date] Not In (Select [Date] From CTE Where Num<=7)
If the number of business days in the table may be less than 7 and you need to bring the total number of days to 7 by adding days off, try this:
Declare #n Int = 7
; With CTE As (
Select [Date], IIF(DATEPART(weekday, [Date]) In (6,7), 0, 1) As IsBusinessDay
From tbl
)
Delete From tbl
Where [Date] Not In (Select Top(#n) [Date] From CTE Order By IsBusinessDay Desc, [Date] Desc)
If there is only one date for each day, you can simply do this:
SELECT TOP 7 [Date] FROM Table1
WHERE
[Date] < GETDATE() AND DATENAME(weekday, [DATE]) NOT IN ('Saturday', 'Sunday')
ORDER BY
[DATE] DESC

Between two date , how can i take all date with one id

empid startdate expirydate
18426 2018-01-01 2018-01-05
i am expecting :-
date id
2018-01-01 18426
2018-01-02 18426
2018-01-03 18426
2018-01-04 18426
2018-01-05 18426
You need cte with recursive way :
with t as (
select empid, startdate, enddate
from table
union all
select empid, dateadd(day, 1, startdate), enddate
from t
where stardate < enddate
)
select *
from t
option (maxrecursion 0);
Use a Calendar Table or a Tally Table.
You can also do it on the fly. This example should get you on the right path:
DECLARE #StartDate date, #EndDate date;
SET #StartDate = '20180101';
SET #EndDate = '20190105';
WITH N AS (
SELECT *
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) V(U)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1
CROSS JOIN N N2
CROSS JOIN N N3
CROSS JOIN N N4)
SELECT DATEADD(DAY, I, #StartDate) AS [date]
FROM Tally T
WHERE DATEADD(DAY, I, #StartDate) <= #EndDate;

Finding missing dates compared to date range

I have one table (A) with date ranges and another (B) with just a set date. There are missing months in B that are within the date range of A. I need to identify the missing months.
A
Person StartDate EndDate
123 1/1/2016 5/1/2016
B
Person EffectiveDate
123 1/1/2016
123 2/1/2016
123 4/1/2016
123 5/1/2016
Expected result would be
123 3/1/2016
I'm using SQL Server 2012. Any assistance would be appreciated. Thanks!
One approach is to generate all values between the two dates. Here is an approach using a numbers table:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master.spt_values
)
select a.person, dateadd(day, n.n, a.startdate) as missingdate
from a join
n
on dateadd(day, n.n, a.startdate) <= day.enddate left join
b
on b.person = a.person and b.effectivedate = dateadd(day, n.n, a.startdate)
where b.person is null;
Try this:
CREATE TABLE #A (Person INT, StartDate DATE, EndDate DATE)
INSERT INTO #A
SELECT '123','1/1/2016', '5/1/2016'
CREATE TABLE #B(Person INT, EffectiveDate DATE)
INSERT INTO #B
SELECT 123 ,'1/1/2016' UNION ALL
SELECT 123 ,'2/1/2016' UNION ALL
SELECT 123 ,'4/1/2016' UNION ALL
SELECT 123 ,'5/1/2016'
;WITH A1
AS(
SELECT PERSON , StartDate, EndDate
FROM #A
UNION ALL
SELECT PERSON ,DATEADD(MM,1,STARTDATE), EndDate
FROM A1
WHERE DATEADD(MM,1,STARTDATE) <= EndDate
)
SELECT PERSON , StartDate
FROM A1
WHERE
NOT EXISTS
(
SELECT 1 FROM #B B1
WHERE B1.Person = A1.PERSON
AND YEAR(B1.EffectiveDate) = YEAR(A1.STARTDATE) AND MONTH(B1.EffectiveDate) = MONTH(A1.STARTDATE)
)
This should work if you are interested in getting missing months
;WITH n
AS (SELECT ROW_NUMBER() OVER(ORDER BY
(
SELECT NULL
)) - 1 AS n
FROM master.dbo.spt_values)
SELECT a.person,
DATEADD(MONTH, n.n, a.startdate) AS missingdate
FROM a a
INNER JOIN n ON DATEADD(MONTH, n.n, a.startdate) <= a.enddate
LEFT JOIN b b ON MONTH(DATEADD(MONTH, n.n, a.startdate)) = MONTH(b.effectivedate) AND YEAR(DATEADD(MONTH, n.n, a.startdate)) = YEAR(b.effectivedate)
WHERE b.person IS NULL;

how to find out days from another table

DECLARE #Daystaken TABLE(
Application VARCHAR(20),
StatusId VARCHAR(2),
Flag INT,
Createddate Datetime)
INSERT INTO #Daystaken (CreatedDate)VALUES
('2015-03-06 17:59:59.410'),
('2015-03-02 17:59:59.410')
select DATEDIFF(DD,MIN(CreatedDate),GETDATE())D from #Daystaken
DECLARE #holiday TABLE(holiday Datetime)
INSERT INTO #holiday ( holiday )VALUES
( '2014-04-06 17:59:59.410'),
( '2014-06-06 17:59:59.410'),
( '2015-05-05 17:59:59.410'),
( '2015-05-01 17:59:59.410'),
( '2013-01-06 17:59:59.410'),
( '2015-02-03 17:59:59.410'),
( '2011-02-01 17:59:59.410')
i have got days count from DaysTaken table Now i need to Substract days from Holidays table.For Example daystaken table have 122 days in that days
3 days holidays till present date came from holidays table.So i need to minus the result and Show 119 Days
Will this do:
DECLARE #minDate AS DATE
SELECT #minDate = MIN(Createddate) FROM #Daystaken;
SELECT DATEDIFF(DAY, #minDate, GETDATE()) - COUNT(*) FROM #holiday
WHERE
CAST(holiday AS DATE) >= #minDate
AND CAST(holiday AS DATE) <= CAST(GETDATE() AS DATE)
Here is another one that does not make use of any variable and can be turned easily into an ITVF:
WITH E1(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
Tally(N) AS(
SELECT TOP (DATEDIFF(DAY, #minDate, GETDATE()))
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E4
),
CteMinDate(minDate) AS(
SELECT CAST(MIN(CreatedDate) AS DATE) FROM #Daystaken
)
SELECT
COUNT(*)
FROM Tally t
CROSS JOIN CteMinDate md
WHERE
DATEADD(DAY, N - 1, md.minDate) <= CAST(GETDATE() AS DATE)
AND NOT EXISTS(
SELECT 1
FROM #holiday
WHERE CAST(holiday AS DATE) = DATEADD(DAY, N - 1, md.minDate)
)

Get all dates in date range in SQL Server

I got this example from one StackOverflow question that was asked but I couldn't get it work according to my need.
WITH DateTable
AS
(
SELECT CAST('20110101' as Date) AS [DATE]
UNION ALL
SELECT DATEADD(dd, 1, [DATE]) FROM DateTable
WHERE DATEADD(dd, 1, [DATE]) < cast('20110131' as Date)
)
SELECT dt.[DATE] FROM [DateTable] dt
Input-
ID | FromDate | ToDate
=============================
1 | 2011-11-10 | 2011-11-12
2 | 2011-12-12 | 2011-12-14
Output -
SN | Dates |
==================
1 | 2011-11-10 |
2 | 2011-11-11 |
3 | 2011-11-12 |
4 | 2011-12-12 |
5 | 2011-12-13 |
6 | 2011-12-14 |
See this code works fine for static dates. But in my case I have a table containing three columns Id, FromDate, ToDate. Now I want to convert each range in the every row to individual dates.
I cannot get the above example to work in case if the range comes from the table and obviously this query has to run for every row in the range table, which is another confusing challenge.
Please help.
With a little help of a numbers table.
declare #T table
(
ID int identity primary key,
FromDate date,
ToDate date
)
insert into #T values
('2011-11-10', '2011-11-12'),
('2011-12-12', '2011-12-14')
select row_number() over(order by D.Dates) as SN,
D.Dates
from #T as T
inner join master..spt_values as N
on N.number between 0 and datediff(day, T.FromDate, T.ToDate)
cross apply (select dateadd(day, N.number, T.FromDate)) as D(Dates)
where N.type ='P'
Try on SE Data
create table Dates (Id int, FromDate date, ToDate date)
insert into Dates values (1, '2011-11-10', '2011-11-12')
insert into Dates values (2, '2011-12-12', '2011-12-14')
with DateTable as
(
select FromDate as Dt, ToDate
from Dates
union all
select DATEADD(D, 1, Dt), ToDate
from DateTable
where DATEADD(D, 1, Dt) <= ToDate
)
select ROW_NUMBER() over (order by Dt) as SN, Dt as Dates
from DateTable
order by Dt
What about this?
--DROP TABLE #Test
CREATE TABLE #Test(ID int, FromDate datetime, ToDate datetime)
INSERT INTO #Test VALUES (1, '2011-11-10', '2011-11-12')
INSERT INTO #Test VALUES (2, '2011-12-12', '2011-12-14')
;
WITH DateTable
AS
(
SELECT ID, FromDate, ToDate, 0 AS Seed FROM #Test
UNION ALL
SELECT ID, DATEADD(dd, 1, FromDate), ToDate, Seed + 1
FROM DateTable
WHERE DATEADD(dd, 1, FromDate) <= ToDate
)
SELECT --*
ROW_NUMBER() OVER (ORDER BY ID, Seed) SN, FromDate AS Dates
FROM DateTable