Possible recursive CTE query using date ranges
You don't need a "real" recursive CTE here. You can use one for the month references though:
;WITH Months
as
(
SELECT DATEADD(day, -DATEPART(day, GETDATE())+1, GETDATE()) as 'MonthStart'
UNION ALL
SELECT DATEADD(month, -1, MonthStart) as 'MonthStart'
FROM Months
)
Then you can JOIN
to SELECT TOP 13 * FROM Months
in your above query.
I'm not going to try to parse all your CASE
statements, but essentially you can use a GROUP BY
on the date and the MonthStart
fields, like:
GROUP BY Datepart(year, monthstart), Datepart(month, monthstart)
and aggregate by month. It will probably be easiest to have all your options (active, lapsed, etc) as columns and calculate each with a SUM(CASE WHEN ... THEN 1 ELSE 0 END)
as it will be easier with a GROUP BY
.
Recursive CTE to split date range
I'm not sure if your last date is suppose to be 3000-01-01 but this should work
CREATE TABLE members (membershipId INT, groupId INT, clientId INT, dateFrom DATETIME, dateTo DATETIME)
INSERT INTO members VALUES
(2707, 20008, 1579, '1997-01-01 00:00:00.000', '1997-12-31 00:00:00.000'),
(20989, 20008, 1579, '1999-01-01 00:00:00.000', '2004-12-31 00:00:00.000'),
(39874, 20298, 1579, '2005-01-01 00:00:00.000', '2008-12-31 00:00:00.000'),
(50295, 21661, 1579, '2009-01-01 00:00:00.000', '2009-12-31 00:00:00.000'),
(50988, 20399, 1579, '2010-01-01 00:00:00.000', '2010-12-31 00:00:00.000'),
(52378, 21661, 1579, '2011-01-01 00:00:00.000', '2013-12-31 00:00:00.000'),
(57274, 21660, 1579, '2014-01-01 00:00:00.000', '3000-01-01 00:00:00.000')
;
WITH cte AS
(
SELECT
membershipId,
groupId,
clientId,
dateFrom,
DATEADD(day, -1, DATEADD(YEAR,1,dateFrom)) newDateTo,
dateTo
FROM
members
UNION ALL
SELECT
m.membershipId,
m.groupId,
m.clientId,
DATEADD(YEAR,1,c.dateFrom),
DATEADD(day, -1, DATEADD(YEAR,2,c.dateFrom)),
c.dateto
FROM
members m
JOIN cte c ON c.membershipId = m.membershipId
AND DATEADD(YEAR,1,c.dateFrom) < m.dateTo
)
SELECT
membershipId,
groupId,
clientId,
dateFrom,
newDateTo dateTo
FROM
cte
ORDER BY
membershipId, dateFrom
OPTION (MAXRECURSION 0);
DROP TABLE members
SQL Fiddle
How to create recursive query to get all dates between two dates
You need recursive CTE:
DECLARE @arrival_date date = '2016-01-01',
@leaving_date date = '2016-02-01'
;WITH cte AS (
SELECT @arrival_date as date_
UNION ALL
SELECT CAST(DATEADD(day,1,date_) as date)
FROM cte
WHERE date_ < @leaving_date
)
SELECT *
FROM cte
OPTION (MAXRECURSION 0)
Output:
date_
2016-01-01
2016-01-02
2016-01-03
...
2016-01-30
2016-01-31
2016-02-01
EDIT1
Based on your sample:
;WITH cte AS (
SELECT GuestID, CAST(ArrivalDate as date) as date_
FROM Guests
UNION ALL
SELECT c.GuestID, CAST(DATEADD(day,1,date_) as date)
FROM cte c
INNER JOIN Guests g
ON g.GuestID = c.GuestID
WHERE date_ < g.LeavingDate
)
SELECT *
FROM cte
ORDER BY GuestID, date_
OPTION (MAXRECURSION 0)
EDIT2
;WITH Guests AS (
SELECT 1 as GuestID,
'2016-01-01' ArrivalDate,
'2016-01-05' LeavingDate
UNION ALL
SELECT 2 ,
'2016-06-17',
'2016-06-20'
), cte AS (
SELECT GuestID, CAST(ArrivalDate as date) as date_
FROM Guests
UNION ALL
SELECT c.GuestID, CAST(DATEADD(day,1,date_) as date)
FROM cte c
INNER JOIN Guests g
ON g.GuestID = c.GuestID
WHERE date_ < g.LeavingDate
)
SELECT *
FROM cte
ORDER BY GuestID, date_
OPTION (MAXRECURSION 0)
Output:
GuestID date_
1 2016-01-01
1 2016-01-02
1 2016-01-03
1 2016-01-04
1 2016-01-05
2 2016-06-17
2 2016-06-18
2 2016-06-19
2 2016-06-20
Create a recursive list of dates given from two Different Dates and items
You can try to use CTE recursion
to make it.
The keypoint is, let startTime
column add one day then convert DateTime
to Date
in the CTE recursion
condition.
CREATE TABLE T(
ID INT,
[Start Date Time] DATETIME,
[End Date Time] DATETIME
);
INSERT INTO T VALUES (1,'2018-08-06 13:00:00.000','2018-08-07 10:00:00.000');
INSERT INTO T VALUES (2,'2018-08-10 08:00:00.000','2018-08-10 15:00:00.000');
Query 1:
;WITH CTE AS (
SELECT ID,[Start Date Time] startTime,[End Date Time] endTime
FROM T
UNION ALL
SELECT ID,CAST(CAST(DATEADD(DAY,1,startTime)AS DATE) AS DATETIME),endTime
FROM CTE
WHERE CAST(CAST(DATEADD(DAY,1,startTime) AS DATE) AS DATETIME) < endTime
)
SELECT id,
startTime AS 'Start Date Time',
(CASE WHEN LEAD(startTime,1,endTime) OVER(PARTITION BY ID ORDER BY startTime)= endTime
THEN endTime
ELSE DATEADD(second,-1,LEAD(startTime,1,endTime) OVER(PARTITION BY ID ORDER BY startTime))
END) AS 'End Date Time'
FROM CTE
Results:
| id | Start Date Time | End Date Time |
|----|----------------------|----------------------|
| 1 | 2018-08-06T13:00:00Z | 2018-08-06T23:59:59Z |
| 1 | 2018-08-07T00:00:00Z | 2018-08-07T10:00:00Z |
| 2 | 2018-08-10T08:00:00Z | 2018-08-10T15:00:00Z |
Recursive CTE Returns Value Outside of Range
Because your Recursive CTE
add one day from fromDate
Select
fromDate = dateadd(day, 1, @EndDate)
but your condition filter fromDate
where
fromDate >= @StartDate AND
fromDate < @EndDate ))
If you want to do make calendar table by Recursive CTE
.
you can try this.
with Dates as (
select @StartDate fromDate,@EndDate endDate
UNION ALL
Select
fromDate = dateadd(day, 1, fromDate),endDate
from
Dates
where
dateadd(day, 1, fromDate) <= @EndDate
)
Select * from Dates
OPTION (MAXRECURSION 0);
Related Topics
How to Get Datetime Value from Timestamp Type Column
SQL Constraint: Date a Is Before Date B -- How
Extract Phone Number from Noised String
How to Create Calculated Field in MySQL
Access Columns of a Table by Index Instead of Name in SQL Server Stored Procedure
MySQL - Change Date String to Date Type in Place
Update Values in Struct Arrays in Bigquery
For Autoincrement Fields: Max(Id) VS Top 1 Id Order by Id Desc
Transact-SQL Shorthand Join Syntax
What Is the Effect of Omitting Size in Nvarchar Declaration
Issues with SQL Comparison and Null Values
With Hibernate, How to Query a Table and Return a Hashmap with Key Value Pair Id>Name
Is Order by Clause Allowed in a Subquery
Right Query to Get the Current Number of Connections in a Postgresql Db
Select Multiple Columns from a Table, But Group by One
Return Rows from Insert with on Conflict Without Needing to Update