Recursive CTE on Dates - sql-server-2005

I want to recursively loop though the temporary table by date1 field using following query in Sql server. I want that the #stardate and #enddate should auto increment up to the #stopcriteria initially starting from
'01/01/2011' to 12/31/2011' and then
'01/01/2012' to 12/31/2012' and so on up to #stopCriteria and resultset should keep joining with union. I have written following query using cte but is not working.
CREATE TABLE #tempbbs576 ( id int, amount money, date1 date)
insert into #tempbbs576(id,amount,date1) values(1,1000,'1/1/2018')
insert into #tempbbs576(id,amount,date1) values(2,20010,'22/3/2019')
insert into #tempbbs576(id,amount,date1) values(3,40010,'1/1/2017')
insert into #tempbbs576(id,amount,date1) values(4,50010,'23/4/2018')
insert into #tempbbs576(id,amount,date1) values(5,60010,'1/1/2018')
insert into #tempbbs576(id,amount,date1) values(6,70010,'21/1/2018')
DECLARE #startDate DATE;
DECLARE #endDate DATE;
DECLARE #stopCriteria DATE;
SET #startDate='01/01/2011'; SET #endDate='12/31/2011'; SET #stopCriteria='01/01/2018';
;WITH ctesequence
AS (SELECT 'Year' + Datename(year, Dateadd(year, 1, #startDate)) /*'2012'*/
AS FiscalYear, Count(DISTINCT id) AS Last2Years
FROM [dbo].[#tempbbs576](nolock) WHERE date1 BETWEEN #startDate AND
#endDate AND id IN (SELECT id FROM [dbo].[#tempbbs576]
WHERE date1 < Dateadd(year, -1, #startDate) AND id
NOT IN (SELECT id FROM [dbo].[#tempbbs576] WHERE date1 BETWEEN
Dateadd(year,-1, #startDate) AND Dateadd( year, -1, #endDate ) ))
UNION
SELECT 'Year' + Datename(year, Dateadd(year, 1, #startDate)) /*'2012'*/
AS FiscalYear, Count(DISTINCT id) AS Last2Years FROM ctesequence WHERE date1 <= #stopCriteria)
SELECT * FROM ctesequence OPTION(maxrecursion 0)
Please let me know if it is possible through cte.
Thanks in Advance,
Amar

Related

How to pass multiple values one by one for a Parameter in an SQL Query and union the results?

I have an SQL Statement which accepts a parameter #EndDate as DateTime. I want to be able to pass several values for #EndDate one by one and then Union the results of all the Queries. I have tried using CTE for this but it is of no use. I want to pass several Dates for #EndDate. Any help would be greatly appreciated.
My Query is:
DECLARE #EndDate as DateTime
SET #EndDate = '2018/02/25'
SELECT
CONVERT(VARCHAR, #EndDate, 101) [R_Date]
,[Name]
[Type]
FROM [dbo].[S_Table]
This is Sample query how u can loop through multiple dates. A while loop should select all rows into one table, do not need to perform union all
DECLARE #table TABLE (
day INT
,Month INT
,DATE DATE
)
DECLARE #startdate DATE = '2018/02/25'
DECLARE #enddate DATE = cast(getdate() AS DATE)
WHILE #startdate <= #enddate
BEGIN
SELECT #startdate = dateadd(dd, 1, #startdate)
INSERT #table (
day
,Month
,DATE
)
SELECT day(#startdate)
,MONTH(#startdate)
,#startdate
END
SELECT *
FROM #table;
In your logic for passing multiple values into query , you can store result set into temporal table, which holds looped records
DECLARE #table TABLE (
[R_Date] DATE
,Name VARCHAR(155)
,Value VARCHAR(155)
)
DECLARE #enddate DATE = '2018/02/25'
DECLARE #enddate1 DATE = cast(getdate() AS DATE)
WHILE #enddate <= #enddate1
BEGIN
SELECT #enddate = dateadd(dd, 1, #enddate)
INSERT #table (
R_Date
,Name
,Value
)
SELECT CONVERT(VARCHAR, #EndDate, 101) [R_Date]
,[Name] [Type]
FROM [dbo].[S_Table]
WHERE DATE = #enddate
-------More filters
END
SELECT *
FROM #table;

SQL using #table/ Temp table inside a table valued function

I need to have a function to get all the week numbers in a given date range.
I have to seperate this from my stored procedure since it has a lengthy process.
But when I use #tables in side the function it gives following error
Msg 2772, Level 16, State 1, Procedure WeekNumbersWithinRange, Line 19
Cannot access temporary tables from within a function.
And here is my function.
CREATE FUNCTION WeekNumbersWithinRange
(
#FromDate DATETIME,
#ToDate DATETIME
)
RETURNS TABLE
AS
BEGIN
IF OBJECT_ID (N'tempdb..#WeeksofRange', N'U') IS NOT NULL
BEGIN
DROP TABLE #WeeksofRange
END
CREATE TABLE #WeeksofRange (WeekNo INT);
-- Get all week numbers for the given delivery date range
IF OBJECT_ID (N'tempdb..#Calendar', N'U') IS NOT NULL
BEGIN
DROP TABLE #Calendar
END
CREATE TABLE #Calendar
(
CalendarDate DATE PRIMARY KEY,
IsWeekend BIT,
YearNo SMALLINT,
QuarterNo TINYINT,
MonthNo TINYINT,
DayOfYearNo SMALLINT,
DayNo TINYINT,
WeekNo TINYINT,
WeekDayNo TINYINT
)
DECLARE #beginDate DATE, #endDate DATE
SELECT
#beginDate = #FromDate, #endDate = #ToDate
WHILE #beginDate <= #endDate
BEGIN
INSERT INTO #Calendar (CalendarDate, IsWeekend, YearNo, QuarterNo, MonthNo, DayOfYearNo, DayNo, WeekNo, WeekDayNo)
SELECT
#beginDate As CalendarDate,
(CASE WHEN DATEPART(Weekday, #beginDate) IN (7, 1)
THEN 1 ELSE 0
END) AS IsWeekend,
DATEPART(Year, #beginDate) AS YearNo,
DATEPART(QUARTER, #beginDate) AS QuarterNo,
DATEPART(MONTH, #beginDate) AS MonthNo,
DATEPART(DayOfYear, #beginDate) AS DayOfYearNo,
DATEPART(Day, #beginDate) AS DayNo,
DATEPART(Week, #beginDate) AS WeekNo,
DATEPART(WEEKDAY, #beginDate) AS WeekDayNo
--,(Case When #beginDate < '02/03/2011' Then 0 Else DATEPART(Week, #beginDate) - 5 End) As mySpecificWeekNo
SET #beginDate = DATEADD(Day, 1, #beginDate)
END
INSERT INTO #WeeksofRange
SELECT DISTINCT WeekNo
FROM #Calendar;
-- End of Select all week numbers in the range
SELECT * FROM #WeeksofRange
GO
Is there any other way to do this? Or do I need to write this logic inside my stored procedure?
You cannot use a Temp table in UDF because
the object can't be created inside UDF.
Instead of using a Temp table, use a table variable, that should solve your problem
CREATE FUNCTION WeekNumbersWithinRange
(
#FromDate DATETIME,
#ToDate DATETIME
)
RETURNS TABLE
AS
BEGIN
DECLARE #WeeksofRange TABLE
(
WeekNo INT
)
-- Get all week numbers for the given delivery date range
declare #Calendar table
(CalendarDate Date Primary key, IsWeekend Bit, YearNo SmallInt, QuarterNo TinyInt, MonthNo TinyInt, DayOfYearNo SmallInt, DayNo TinyInt, WeekNo TinyInt, WeekDayNo TinyInt )
Declare #beginDate Date, #endDate Date
Select #beginDate = #FromDate , #endDate = #ToDate
While #beginDate <= #endDate
Begin
Insert Into #Calendar (CalendarDate, IsWeekend, YearNo, QuarterNo, MonthNo, DayOfYearNo, DayNo, WeekNo, WeekDayNo)
Select
#beginDate As CalendarDate
,(Case When DATEPART(Weekday, #beginDate) In (7, 1) Then 1 Else 0 End) As IsWeekend
,DATEPART(Year, #beginDate) As YearNo
,DATEPART(QUARTER, #beginDate) As QuarterNo
,DATEPART(MONTH, #beginDate) As MonthNo
,DATEPART(DayOfYear, #beginDate) As DayOfYearNo
,DATEPART(Day, #beginDate) As DayNo
,DATEPART(Week, #beginDate) As WeekNo
,DATEPART(WEEKDAY, #beginDate) As WeekDayNo
--,(Case When #beginDate < '02/03/2011' Then 0 Else DATEPART(Week, #beginDate) - 5 End) As mySpecificWeekNo
Set #beginDate = DateAdd(Day, 1, #beginDate)
End
INSERT INTO #calendar
SELECT DISTINCT WeekNo FROM #Calendar;
-- End of Select all week numbers in the range
SELECT * FROM #WeeksofRange
GO
I'd normally choose to make the Calendar table permanent and populate it with, say, a centuries worth of rows. And then just query it.
But, if you have to have this function, you can write it to use a CTE and avoid any explicit state/iteration management:
CREATE FUNCTION WeekNumbersWithinRange
(
#FromDate DATETIME,
#ToDate DATETIME
)
RETURNS TABLE
AS
return (With Weeks as (
select DATEPART(week,#FromDate) as Week,#FromDate as dt
union all
select DATEPART(week,ndt),ndt
from Weeks
cross apply (select DATEADD(day,7,dt) as ndt) t
where
ndt < #ToDate
)
select Week from Weeks
union
select DATEPART(week,#ToDate)
)
I've made the simplifying assumption that week numbers only change every 7 days so we don't need to generate a row for every day inside the CTE. Also, I use the final UNION to make sure that the final week is included. It could be skipped since I'm working in 7 day increments, so we could jump our value straight from a previous week to a value greater than #ToDate.
Depending on the ranges you're working with, you may have to apply the MAXRECURSION option to the query to allow the CTE to work fully.

Add one month until it is greater than some value

I have start date & Mdate as columns in table, i want to do something like below in SQL
Add 1 month to Start_Date until start date > Mdate
I tried using while and if concepts, but no luck.
DECLARE #MIGRATIONDATE DATE, #STRT_DATE DATE, #NEXTD DATE
SET #MIGRATIONDATE =20140725
SET #STRT_DATE = 20140521
SELECT WHILE ( #STRT_DATE > #MIGRATIONDATE)
BEGIN
DATEADD(MM,1,#STRT_DATE))
END
appreciate if you can guide me on this?
I do not know if you would like to get one row of that table. If you want to do it for each row, then you should wrap it in a function and use CROSS APPLY.
declare
#startdate datetime,
#enddate datetime
set #startdate = '20140101'
set #enddate = '20150101'
;WITH date_range (thedate) AS (
select #startdate
UNION ALL SELECT DATEADD(MONTH, 1, thedate)
FROM date_range
WHERE DATEADD(MONTH, 1, thedate) <= #enddate
)
SELECT thedate FROM date_range
If you wanted in a function:
CREATE FUNCTION [dbo].[ExplodeDates](#startdate datetime, #enddate datetime)
returns table as
return (
WITH date_range (thedate) AS (
select #startdate
UNION ALL SELECT DATEADD(DAY, 1, thedate)
FROM date_range
WHERE DATEADD(DAY, 1, thedate) <= #enddate
)
SELECT thedate FROM date_range
);
SELECT Id,thedate
FROM Table1 T1
CROSS APPLY [dbo].[ExplodeDates](T1.StartDate,T1.EndDate)
select
case when start_date<Mdate then dateadd(mm,1,start_date) else start_date end
from yourTable
suppose start_date is 20140721 and MigrationDate is 20140525 then it returns you with gives you accepted result
select case
when convert( date,'20140721') <convert(date,'20140525')
then dateadd(mm,1,'20140721') else '20140721' end
How about (SQL Fiddle):
SELECT DATEADD(month,
DATEDIFF(month, Start_Date, Mdate) + 1,
Start_Date) AS NEW_START_DATE
FROM MyTable;
If there is a possibility of the Start_Date being greater than or equal to Mdate then use the following (SQL Fiddle):
SELECT Start_Date AS OLD_START_DATE,
CASE WHEN DATEDIFF(month, Start_Date, Mdate) > 0
THEN DATEADD(month, DATEDIFF(month, Start_Date, Mdate) + 1, Start_Date)
ELSE Start_Date END AS NEW_START_DATE,
Mdate
FROM MyTable;

SQL: How to create a temp table and fill it with date within a select-from statement

I wish to create a temp table with 1 datetime column and then fill it with date(30 days before today). I wish to do all these in a select-from statement.
I could do it with a "WITH" loop as below prior to the select-from statement. However, I wish to do it within a select-from statement.
declare #endDate datetime
set #endDate = dateadd(day,-30,getdate())
with CTE_Table (
Select dataDate = dateadd(day,-1,getdate()) from CTE_Table
where datediff(day,dataDate,#endDate) < 0
)
select * from CTE_Table
Please help... :....(
You can use SELECT ... INTO.
BTW Your recursive CTE is invalid. A fixed version is below
DECLARE #endDate DATETIME
SET #endDate = dateadd(day, -30, getdate());
WITH CTE_Table(dataDate)
AS (SELECT dateadd(day, -1, getdate())
UNION ALL
SELECT dateadd(day, -1, dataDate)
FROM CTE_Table
WHERE datediff(day, dataDate, #endDate) < 0)
SELECT dataDate
INTO #T
FROM CTE_Table
You could do:
CREATE TABLE #temptable
(
DateColumn DATETIME
)
INSERT INTO #temptable
SELECT dataDate FROM CTE_Table

Split Date Range in months SQL Server

I want to split date range in months. I will pass startdate(1-jan-2011) and enddate(31-dec-2011) as a parameter then it must return result like
1-jan-2011 - 31-jan-2011
1-feb-2011 - 28-feb-2011
1-mar-2011 - 31-mar-2011
Please send me a stored procedure.....
Thanks,
Abhishek
Try this:
CREATE PROC SplitDateRange
#from DATETIME,
#to DATETIME
AS
BEGIN
SET NOCOUNT ON;
SET #from = CONVERT(VARCHAR, DATEADD(DAY, -DATEPART(DAY, #from)+1, #from), 112)
-- Sql 2000
CREATE TABLE #temp (DateFrom DATETIME, DateTo DATETIME)
WHILE #from < #to
BEGIN
INSERT #temp VALUES (#from, DATEADD(DAY, -1, DATEADD(MONTH, 1, #from)))
SET #from = DATEADD(MONTH, 1, #from)
END
SELECT * FROM #temp
DROP TABLE #temp
--sql 2005+
/*
;WITH Ranges(DateFrom, DateTo) AS
(
SELECT #from DateFrom, DATEADD(DAY, -1, DATEADD(MONTH, 1, #from)) DateTo
UNION ALL
SELECT DATEADD(MONTH, 1, DateFrom), DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(MONTH, 1, DateFrom)))
FROM Ranges
WHERE DateFrom < DATEADD(MONTH, -1, #To)
)
SELECT * FROM Ranges
OPTION(MAXRECURSION 0)
*/
END
GO
EXEC SplitDateRange '2011-01-02', '2012-06-06'
So that you can use the results in another SQL Query (which I assume is where you're going) I'd put that into a table valued function.
Assuming SQL Server 2005+ you could use this...
CREATE FUNCTION dbo.ufnMonthlyIntervals(
#from_date SMALLDATETIME,
#end_date SMALLDATETIME
)
RETURNS TABLE
WITH
intervals (
from_date,
end_date
)
AS
(
SELECT #from_date, DATEADD(MONTH, 1, #from_date ) - 1
UNION ALL
SELECT end_date + 1, DATEADD(MONTH, 1, end_date + 1) - 1 FROM intervals WHERE end_date < #end_date
)
RETURN
SELECT
from_date,
CASE WHEN end_date > #end_date THEN #end_date ELSE end_date END AS end_date
FROM
intervals
Then you just use SELECT * FROM dbo.ufnMonthlyIntervals('20110101', '20111201') AS intervals