I have data in a row as below in SQL (2012) table
ID Value StartDate EndDate
123 5000 14/04/2017 15/12/2017
I would like to Split the row into each individual month as below:
ID Value StartDate EndDate
123 5000 14/04/2017 30/04/2017
123 5000 01/05/2017 31/05/2017
123 5000 01/06/2017 30/06/2017
123 5000 01/07/2017 31/07/2017
123 5000 01/08/2017 30/08/2017
123 5000 01/09/2017 31/09/2017
123 5000 01/10/2017 31/10/2017
123 5000 01/11/2017 30/11/2017
123 5000 01/12/2017 15/12/2017
Appreciate help in this matter.
You can do it like this:
WITH tally
AS (SELECT TOP (1000)
ROW_NUMBER() OVER (ORDER BY t1.object_id) AS N
FROM master.sys.all_columns t1
CROSS JOIN master.sys.all_columns t2),
rowset
AS (SELECT ID,
Value,
DATEADD(m, N - 1, StartDate) AS StartDate,
Enddate,
N
FROM tally, MyDates
WHERE DATEFROMPARTS(YEAR(DATEADD(m, N - 1, StartDate)),
MONTH(DATEADD(m, N - 1, StartDate)), 1) <= EndDate)
SELECT Id,
Value,
CASE
WHEN N = 1 THEN
StartDate
ELSE
DATEFROMPARTS(YEAR(StartDate), MONTH(StartDate), 1)
END AS StartDate,
CASE
WHEN DATEADD(d, -DAY(DATEADD(m, 1, StartDate)), DATEADD(m, 1, StartDate)) <= EndDate THEN
DATEADD(d, -DAY(DATEADD(m, 1, StartDate)), DATEADD(m, 1, StartDate))
ELSE
Enddate
END AS EndDate
FROM rowset
ORDER BY ID, StartDate;
Use below query to get the result
DECLARE #Temp TABLE
(
ID INT
,Value NUMERIC(10,2)
,StartDate DATE
,EndDate DATE
)
DECLARE #NewTable TABLE
(
id INT IDENTITY(1,1)
,item INT
,Value NUMERIC(10,2)
,StartDate DATE
,EndDate DATE
)
INSERT INTO #Temp
SELECT 123,5000 ,'2017-04-14','2018-12-15'
DECLARE #SDate date
DECLARE #EDate date
declare #LastDay INT =0
SELECT top 1 #SDate=StartDate,#EDate=EndDate FROM #Temp
declare #Year int = 0
declare #Month int = 0
declare #Index INT =1
DECLARE #Lenght int =DATEDIFF(MONTH,#SDate,#EDate) +1
WHILE #Index<=#Lenght
BEGIN
IF #Index=1 BEGIN
-- first record
SELECT #Year= YEAR(#SDate)
SELECT #Month = MONTH(#SDate)
-- get last day of the date
SELECT #LastDay = day(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#SDate)+1,0)))
SELECT #EDate= CONVERT(VARCHAR(10),#Year)+'-'+CONVERT(VARCHAR(10),#Month)+'-
'+CONVERT(VARCHAR(10),#LastDay)
INSERT INTO #NewTable
SELECT 123,5000,#SDate, #EDate
END ELSE IF #Index = #Lenght BEGIN
SELECT #Month = #Month+1
IF #Month>12 BEGIN
SELECT #Month =1
SELECT #Year =#Year +1
END ELSE BEGIN
SELECT #Year= YEAR(#EDate)
END
SELECT #SDate =CONVERT(VARCHAR(10),#Year)+'-'+CONVERT(VARCHAR(10),#Month)+ '-01'
INSERT INTO #NewTable
SELECT 123,5000 ,#SDate,#EDate
END ELSE BEGIN
SELECT #Month = #Month+1
if #Month>12 BEGIN
SELECT #Month =1
SELECT #Year =#Year +1
END
SELECT #SDate =CONVERT(VARCHAR(10),#Year)+'-'+CONVERT(VARCHAR(10),#Month)+ '-01'
SELECT #LastDay = day(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#SDate)+1,0)))
SELECT #EDate= CONVERT(VARCHAR(10),#Year)+'-'+CONVERT(VARCHAR(10),#Month)+'-'+CONVERT(VARCHAR(10),#LastDay)
INSERT INTO #NewTable
SELECT 123,5000 ,#SDate,#EDate
END
SELECT #Index=#Index+1
end
SELECT * from #NewTable
--select day(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,getdate())+1,0)))
Related
When I call this with these parameters(#MediaSellerID = 138, #StartDate = N'9/1/2020', #EndDate = N'10/7/2020') it returns data successfully.
But when I passed these parameters(#MediaSellerID = 213,#StartDate = '8/4/2020',#EndDate = N'11/25/2020')
this error occurs.
Msg 530, Level 16, State 1, Procedure GetAvailableDatesforCampaign, Line 81 [Batch Start Line 2]
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
If anyone has this solution (OPTION(MAXRECURSION 0)) then please also mention this statement place. thanks
ALTER Procedure [dbo].[GetAvailableDatesforCampaign]
-- exec GetAvailableDatesforCampaign 2,getdate(),getdate()
#MediaSellerID int,
#StartDate datetime,
#EndDate datetime
AS
BEGIN
SET FMTONLY OFF
Declare #Frequency as varchar(200)
Declare #Weeks as varchar(200)
Declare #Days as varchar(200)
Declare #CopyDeadlineDays as int
Declare #DayItem as varchar(20)
Declare #WeekItem as varchar(200)
Declare #DayNumber as int
Delete from tblCampaignDates
select
#Frequency=MediaFrequency,
#Weeks=BroadcastDays,
#Days=BroadcastDaysWeeks,
#CopyDeadlineDays=Copydeadline
from
tblMediaSeller where MediaSeller_ID=#MediaSellerID
CREATE TABLE
#TempAvailableDates
(
AvailableDate datetime default GetDate()
)
CREATE TABLE
#TempAllDates
(
AllDate datetime,
DayNames varchar(20),
Weeknumber int
)
IF(#Frequency='Bi-Weekly' or #Frequency='Weekly' or #Frequency='Monthly')
BEGIN
DECLARE MY_CURSOR2 CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT DISTINCT
Item
FROM
dbo.SplitString(#Days)
OPTION (MAXRECURSION 0)
OPEN MY_CURSOR2
FETCH NEXT FROM MY_CURSOR2 INTO #DayItem
WHILE ##FETCH_STATUS = 0
BEGIN
-- Select All dates of those days in tblMediaSeller (Adv Publishing day) from start date to End dates
Set #DayNumber= CASE WHEN #DayItem='Monday' THEN 1
WHEN #DayItem='Tuesday' THEN 2
WHEN #DayItem='Wednesday' THEN 3
WHEN #DayItem='Thursday' THEN 4
WHEN #DayItem='Friday' THEN 5
WHEN #DayItem='Saturday' THEN 6
WHEN #DayItem='Sunday' THEN 7
ELSE 0 END
IF(#DayNumber<>0)
BEGIN
SET DATEFIRST #DayNumber; -- First day of the week is set to monday
WITH CTE(dt)
AS
(
SELECT #StartDate
UNION ALL
SELECT DATEADD(d, 1, dt)
FROM CTE
WHERE dt <#EndDate
)
INSERT INTO
#TempAllDates
SELECT
CONVERT(date,dt),
datename(dw, dt),
DATEDIFF(WEEK, DATEADD(MONTH, DATEDIFF(MONTH, 0, dt), 0), dt)
FROM
CTE
WHERE
datepart ("dw", dt) = 1;
END
FETCH NEXT FROM MY_CURSOR2 INTO #DayItem
END
CLOSE MY_CURSOR2
DEALLOCATE MY_CURSOR2
Set #WeekItem= REPLACE(#Weeks, 'Week', '');
DECLARE #ActualStartDate as Datetime
Set #ActualStartDate = (Select top 1 AllDate from #TempAllDates)
IF(#CopyDeadlineDays is null)
BEGIN
IF(#ActualStartDate>= DATEADD(day,5,getdate()))
BEGIN
Insert into
#TempAvailableDates
Select
AllDate
From
#TempAllDates
WHERE
Weeknumber in (select cast(item as int) from dbo.SplitString(#WeekItem))
END
ELSE
BEGIN
Insert into
#TempAvailableDates
Select
AllDate
From
#TempAllDates
WHERE
Weeknumber in (select cast(item as int) from dbo.SplitString(#WeekItem))
AND AllDate>=DATEADD(day,5,getdate())
END
END
ELSE
BEGIN
IF(#ActualStartDate>= DATEADD(day,#CopyDeadlineDays,getdate()))
BEGIN
Insert into
#TempAvailableDates
Select
AllDate
From
#TempAllDates
WHERE
Weeknumber in (select cast(item as int) from dbo.SplitString(#WeekItem))
END
ELSE
BEGIN
Insert into
#TempAvailableDates
Select
AllDate
From
#TempAllDates
WHERE
Weeknumber in (select cast(item as int) from dbo.SplitString(#WeekItem))
AND AllDate>=DATEADD(day,#CopyDeadlineDays,getdate())
END
END
END
ELSE
BEGIN
IF(#CopyDeadlineDays is null)
begin
IF(#StartDate>= DATEADD(day,5,getdate()))
BEGIN
Insert into
#TempAvailableDates
SELECT TOP
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b;
END
ELSE
BEGIN
SET #StartDate=#StartDate+5
Insert into
#TempAvailableDates
SELECT TOP
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b;
END
END
ELSE
BEGIN
IF(#StartDate>= DATEADD(day,#CopyDeadlineDays,getdate()))
BEGIN
Insert into
#TempAvailableDates
SELECT TOP
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b;
END
ELSE
BEGIN
SET #StartDate=#StartDate+#CopyDeadlineDays
Insert into
#TempAvailableDates
SELECT TOP
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b;
END
END
END
Insert into
tblCampaignDates
Select
AvailableDate
from #TempAvailableDates
Select AvailableDate from tblCampaignDates
END
This should work:
WITH CTE(dt) AS (
SELECT #StartDate
UNION ALL
SELECT DATEADD(d, 1, dt)
FROM CTE
WHERE dt <#EndDate
)
INSERT INTO #TempAllDates
SELECT CONVERT(date,dt), datename(dw, dt), DATEDIFF(WEEK, DATEADD(MONTH, DATEDIFF(MONTH, 0, dt), 0), dt)
FROM CTE
WHERE datepart(dw, dt) = 1
OPTION (MAXRECURSION 0);
Here is a db<>fiddle with a much simpler example.
Thanks for all for reading my questions, I have fallen a big problem to retrieve date from SQL Server 2012 by providing year, week number and day name.
Suppose I have
Year = 2016
Week number = 1
Day Name ='FRI'
First day of week='SUN'
Expected result:
01-01-2016
How can I do that?
EDIT: I have found similar solution from here but I have no month name.
My suggestion is based on the solution to the question in the link you provided.
Basically, I've created a calendar that holds the dates since January 1st of the year till #x weeks after that, and then queried that calendar:
-- provided data:
DECLARE #Year int = 2016,
#WeekNumber int = 1,
#DayName char(3) = 'Fri';
-- Calculate start date and end date
DECLARE #StartDate date,
#EndDate date;
SELECT #StartDate = CAST('01-01-'+ CAST(#Year as char(4)) as date),
#EndDate = DATEADD(WEEK, #WeekNumber, #StartDate)
-- Create the calendar
;WITH CTE AS
(
SELECT #StartDate as TheDate
UNION ALL
SELECT DATEADD(DAY, 1, TheDate)
FROM CTE
WHERE DATEADD(DAY, 1, TheDate) <= #EndDate
)
-- Finally, query the calendar:
SELECT TheDate
FROM CTE
WHERE DATEPART(WEEK, TheDate) = #WeekNumber
AND YEAR(TheDate) = #Year
AND DATENAME(WEEKDAY, TheDate) LIKE #DayName + '%'
OPTION(MAXRECURSION 0)
results:
TheDate
----------
2016-01-01
Note: This solution will return no rows if the day you specify is mon, since the first week of 2016 starts on Friday.
TRY THIS
DECLARE #Year varchar(4)
DECLARE #WeekDayday varchar(10)
DECLARE #WeekNumber int
SET #Year ='2016'
SET #WeekDayday ='fri'
SET #WeekNumber =1
--used to solve
DECLARE #StartDate datetime
,#EndDate datetime
,#FirstWeek int
SET #StartDate='01-01-'+' '+#Year
SET #EndDate=#StartDate+38
SET #FirstWeek=DATENAME(week,#StartDate)-1
;with AllDates AS
(
SELECT #StartDate AS DateOf, DATENAME(week,#StartDate)-#FirstWeek AS WeekOf, DATENAME(weekday,#StartDate) AS WeekDayOf
UNION ALL
SELECT DateOf+1, DATENAME(week,DateOf+1)-#FirstWeek AS WeekOf, DATENAME(weekday,DateOf+1) AS WeekDayOf
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT
DateOf
FROM AllDates
WHERE WeekOf=#WeekNumber AND WeekDayOf LIKE #WeekDayday+'%'
ORDER BY DateOf
How about improve that answer by evaluate month name by week number:
--given info
DECLARE #Year varchar(4);
DECLARE #MonthName varchar(10);
DECLARE #WeekDayday varchar(10);
DECLARE #WeekNumber int;
SET #Year = '2016';
SET #WeekDayday = 'Tue';
set #WeekNumber = 46;
-- get month number by week
declare #w int = 0;
declare #m int = 1;
while (#w <= #WeekNumber and #m < 13) begin
set #w = datepart(wk, datefromparts(#year, #m, 1));
if (#w <= #WeekNumber and #m < 13) begin
set #m = #m + 1;
end;
end;
set #m = #m -1;
-- get month name
set #MonthName = left(DateName(month ,DateAdd(month ,#m ,0 ) - 1), 3);
--used to solve
DECLARE #StartDate datetime
,#EndDate datetime
,#FirstWeek int
SET #StartDate='01 '+#MonthName+' '+#Year
SET #EndDate=#StartDate+38
SET #FirstWeek=DATENAME(week,#StartDate)-1
;with AllDates AS
(
SELECT #StartDate AS DateOf, DATENAME(week,#StartDate)-#FirstWeek AS WeekOf, DATENAME(weekday,#StartDate) AS WeekDayOf
UNION ALL
SELECT DateOf+1, DATENAME(week,DateOf+1)-#FirstWeek AS WeekOf, DATENAME(weekday,DateOf+1) AS WeekDayOf
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT
DateOf ,WeekOf ,WeekDayOf
FROM AllDates
WHERE datepart(wk, DateOf) = #WeekNumber AND WeekDayOf LIKE #WeekDayday+'%'
ORDER BY DateOf
I have a sql table that has the following
ID StartDate EndDate
10 2015-12-01 2016-05-31
15 2016-01-05 2016-07-04
20 2016-02-10 2016-08-09
I need to break down the months like so...
ID StartDate EndDate
10 2015-12-01 2015-12-31
10 2016-01-01 2016-01-31
10 2016-02-01 2016-02-29
10 2016-03-01 2016-03-31
10 2016-04-01 2016-04-30
10 2016-05-01 2016-05-31
15 2016-01-05 2016-02-04
15 2016-02-05 2016-03-04
15 2016-03-05 2016-04-04
15 2016-04-05 2016-05-04
15 2016-05-05 2016-06-04
15 2016-06-05 2016-07-04
etc
I'm new to SQL so an example would be very helpful
Calendar
recommended if you have persistent Calendar/DateRanges table
declare #datebegin date = '20140101'
;with cteCalendar as
(
select
c.period_start,
dateadd(dd, -1, dateadd(mm, 1, c.period_start)) as period_end
from
(
select top 100
dateadd(mm, row_number() over(order by sc.object_id)-1, #datebegin) as period_start
from sys.columns sc
order by period_start
) c
),
cteData as
(
select cast(10 as int) as id, cast('20151201' as date) as StartDate, cast('20160531' as date) as EndDate
union all
select 15, '20160105', '20160704'
union all
select 25, '20160210', '20160809'
),
cteDataEx as
(
select d.id, d.StartDate, d.EndDate, datepart(dd, d.StartDate)-1 as DateOffset
from cteData d
)
select
d.id,
dateadd(dd, d.DateOffset, c.period_start) as StartDate,
dateadd(dd, d.DateOffset, c.period_end) as EndDate
from cteDataEx d
inner join cteCalendar c on c.period_start <= d.EndDate and c.period_end >= d.StartDate
where dateadd(dd, d.DateOffset, c.period_end) <= d.EndDate
order by id, StartDate
Actually I did not notice at the beginning that periods may start and end not at 1st day of month, so had to append some calculations after completion of the whole script. Later I realized that <= >= date filter produces unnecessary last row which overflows original date range high bound. So had to append final filter and after that modification don't like this approach totally )) May be some enhancements can be applied but I'm not interested in. Lots of ways to accomplish this task. Additional information about nature and purpose of periods given may alter relevance and applicability of different approaches
Recursion
no extra data required but recursion can be slow if date ranges can be wide enough.
;with cteData as
(
select cast(10 as int) as id, cast('20151201' as date) as StartDate, cast('20160531' as date) as EndDate
union all
select 15, '20160105', '20160704'
union all
select 25, '20160210', '20160809'
),
ctePeriods as
(
select
d.id,
d.StartDate,
dateadd(dd, -1, dateadd(mm, 1, d.StartDate)) as EndDate,
d.EndDate as _EndDate
from cteData d
union all
select
p.id,
dateadd(mm, 1, p.StartDate),
dateadd(dd, -1, dateadd(mm, 2, p.StartDate)),
p._EndDate
from ctePeriods p
where p.EndDate < p._EndDate
)
select p.id, p.StartDate, p.EndDate
from ctePeriods p
order by id, startdate
this code generate the rage of months, inclute leap year, but I don't undestand your need so explain better
create table #dia_meses
(mes int,
messtr varchar(2),
dia_final varchar(2))
insert into #dia_meses values(1,'01','31')
insert into #dia_meses values(2,'02','29')
insert into #dia_meses values(3,'03','31')
insert into #dia_meses values(4,'04','30')
insert into #dia_meses values(5,'05','31')
insert into #dia_meses values(6,'06','30')
insert into #dia_meses values(7,'07','31')
insert into #dia_meses values(8,'08','31')
insert into #dia_meses values(9,'09','30')
insert into #dia_meses values(10,'10','31')
insert into #dia_meses values(11,'11','30')
insert into #dia_meses values(12,'12','31')
declare #year varchar(4)
declare #contador int
set #year =convert(varchar,DATEPART(YEAR,GETDATE()))
set #contador =convert(varchar,DATEPART(month,GETDATE()))
declare #dataIni datetime
declare #datafim datetime
set #dataIni=(select #year+'-'+messtr+'-01' from #dia_meses where mes=#contador)
--pulo do gato ano bissexto
if(#contador=2)
begin
if(select ISDATE(#year+'-'+messtr+'-'+dia_final) from #dia_meses where mes=#contador)=0
begin
set #datafim=(select #year+'-'+messtr+'-28' from #dia_meses where mes=#contador)
end
else--ano bissexto
begin
set #datafim=(select #year+'-'+messtr+'-'+dia_final from #dia_meses where mes=#contador)
end
end
else
begin
set #datafim=(select #year+'-'+messtr+'-'+dia_final from #dia_meses where mes=#contador)
end
print #dataIni
print #dataFim
This will work on SQL Server 2012 and up; the EOMONTH function does not exist on earlier versions.
DECLARE #table TABLE (ID INT, StartDate DATE, EndDate DATE)
DECLARE #outtable TABLE (ID INT, StartDate DATE, EndDate DATE)
DECLARE #ID INT
DECLARE #StartDate DATE
DECLARE #Date1 DATE
DECLARE #Date2 DATE
DECLARE #EndDate DATE
INSERT INTO #table VALUES
(10,'2015-12-01','2016-05-31')
,(15,'2016-01-05','2016-07-04')
,(20,'2016-02-10','2016-08-09')
DECLARE tablecursor CURSOR FOR
SELECT * FROM #table
OPEN tablecursor
FETCH NEXT FROM tablecursor INTO #ID, #StartDate, #EndDate
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Date1 = #StartDate
SET #Date2 = EOMONTH(#Date1)
WHILE #Date1 < #EndDate
BEGIN
PRINT CONVERT(VARCHAR,#ID) + ' ' + CONVERT(VARCHAR,#Date1) + ' ' + CONVERT(VARCHAR,#Date2)
INSERT INTO #outtable
SELECT #ID, #Date1, #Date2
SET #Date1 = DATEADD(DAY,1,#Date2)
SET #Date2 = EOMONTH(#Date1)
IF #Date2 > #EndDate
BEGIN
SET #Date2 = #EndDate
END
END
FETCH NEXT FROM tablecursor INTO #ID, #StartDate, #EndDate
END
SELECT * FROM #outtable
CLOSE tablecursor
DEALLOCATE tablecursor
I need to display dates of all Mondays in the given date range.
For example, if my start date is 01/05/2015 and end date is 31/05/2015, I need to show
04/05/2015
11/05/2015
18/05/2015
25/05/2015
How is it possible?
This procedure is independent from regions and languages.
Please note the first line with SET DATEFIRST 1.
SET DATEFIRST 1; -- First day of the week is set to monday
DECLARE #DateFrom DateTime ='20150601', #DateTo DateTime = '20150630' ;
WITH CTE(dt)
AS
(
SELECT #DateFrom
UNION ALL
SELECT DATEADD(d, 1, dt) FROM CTE
WHERE dt < #DateTo
)
SELECT dt FROM CTE where datepart ("dw", dt) = 1;
Using a CTE it is possible this way..
DECLARE #DateFrom DateTime ='2015-05-01',
#DateTo DateTime = '2015-05-31'
;WITH CTE(dt)
AS
(
SELECT #DateFrom
UNION ALL
SELECT DATEADD(d, 1, dt) FROM CTE
WHERE dt < #DateTo
)
SELECT 'Monday', dt FROM CTE
WHERE DATENAME(dw, dt) In ('Monday')
Refer: Select dates of a day between two dates.
SELECT [Day],[Dt] FROM dbo.fnGetDatesforAday('7/1/2008','8/31/2008','Sunday')
CREATE FUNCTION fnGetDatesforAday
(
-- Add the parameters for the function here
#DtFrom DATETIME,
#DtTo DATETIME,
#DayName VARCHAR(12)
)
RETURNS #DateList TABLE ([Day] varchar(20),Dt datetime)
AS
BEGIN
IF NOT (#DayName = 'Monday' OR #DayName = 'Sunday' OR #DayName = 'Tuesday' OR #DayName = 'Wednesday' OR #DayName = 'Thursday' OR #DayName = 'Friday' OR #DayName = 'Saturday')
BEGIN
--Error Insert the error message and return
INSERT INTO #DateList
SELECT 'Invalid Day',NULL AS DAT
RETURN
END
DECLARE #TotDays INT
DECLARE #CNT INT
SET #TotDays = DATEDIFF(DD,#DTFROM,#DTTO)-- [NO OF DAYS between two dates]
SET #CNT = 0
WHILE #TotDays >= #CNT -- repeat for all days
BEGIN
-- Pick each single day and check for the day needed
IF DATENAME(DW, (#DTTO - #CNT)) = #DAYNAME
BEGIN
INSERT INTO #DateList
SELECT #DAYNAME,(#DTTO - #CNT) AS DAT
END
SET #CNT = #CNT + 1
END
RETURN
END
SET DATEFIRST 7; -- Set's sunday as first day of week, won't work otherwise
DECLARE #StartDate DATE = '06/01/2015'
DECLARE #EndDate DATETIME = '06/30/2015'
DECLARE #TableOfDates TABLE(DateValue DATETIME)
DECLARE #CurrentDate DATETIME
SET #CurrentDate = #startDate
WHILE #CurrentDate <= #endDate
BEGIN
INSERT INTO #TableOfDates(DateValue) VALUES (#CurrentDate)
SET #CurrentDate = DATEADD(DAY, 1, #CurrentDate)
END
SELECT * FROM #TableOfDates WHERE DATEPART(weekday,Datevalue) = 2
Referring back to this SO post
If there is a Grouping category "Category" which, for simplicity's sake, can be either X or Y - is it a trivial matter amending this script so that it will add in the missing dates for each of the categories ?
I assume the category will need adding into the CTE?
In other words if I have the following initial table:
...how do I get to the following:
Will upload my attempt shortly
I've called the initial table #x. I'm hoping to adapt a recursive CTE query like the following to include the field Category:
DECLARE #MinDate DATETIME;
SET #MinDate = (SELECT Min(DATE) FROM #x)
DECLARE #MaxDate DATETIME;
SET #MaxDate = (SELECT Max(DATE) FROM #x)
;WITH times AS
(
SELECT #MinDate dt , 1 depth
UNION ALL
SELECT
DATEADD(d, depth, #MinDate) dt
, 1 + depth as depth
FROM times
WHERE DATEADD(d, depth, #MinDate) <= #MaxDate
)
SELECT
*
FROM
TIMES t
LEFT OUTER JOIN #X x
ON
t.dt = x.Date
Ok - I've tied including a CROSS JOIN but it expands things incorrectly:
SELECT DISTINCT Category INTO #Cat FROM #x
DECLARE #MinDate DATETIME;
SET #MinDate = (SELECT Min(DATE) FROM #x)
DECLARE #MaxDate DATETIME;
SET #MaxDate = (SELECT Max(DATE) FROM #x)
;WITH times AS
(
SELECT
Category
, #MinDate dt
, 1 depth
FROM #Cat
UNION ALL
SELECT
c.Category
, DATEADD(d, depth, #MinDate) dt
, 1 + depth as depth
FROM
times t
CROSS JOIN #Cat c
--ON c.Category IS NOT NULL
WHERE DATEADD(d, depth, #MinDate) <= #MaxDate
)
SELECT
*
FROM
TIMES
This seems to have worked ok:
SELECT DISTINCT Category INTO #Cat FROM #x
DECLARE #MinDate DATETIME;
SET #MinDate = (SELECT Min(DATE) FROM #x)
DECLARE #MaxDate DATETIME;
SET #MaxDate = (SELECT Max(DATE) FROM #x)
;WITH times AS
(
SELECT
Category
, #MinDate dt
, 1 depth
FROM #Cat
UNION ALL
SELECT
Category
, DATEADD(d, depth, #MinDate) dt
, 1 + depth as depth
FROM
times t
WHERE DATEADD(d, depth, #MinDate) <= #MaxDate
)
SELECT
*
FROM
TIMES
Here is a solution without a calendar table (which is a must in production). You might have date range in variables, or you might go for min() and max() from the_table.
EDIT: shorter version incorporating categories into date range generation
declare #startdate datetime = '2012-1-1'
declare #enddate datetime = '2012-1-5'
; with dates([date], category) as (
select distinct #startdate, category
from the_table
union all
select dateadd (day, 1, [date]), category
from dates
where [date] < #enddate
)
select dates.date,
dates.category,
isnull(the_table.amount, 0) Amount
from dates
left join the_table
on dates.date = the_table.date
and dates.category = the_table.category
order by dates.category, dates.date
option (maxrecursion 0)
There is live test # Sql Fiddle.
New Sql Fiddle.
Something like this should do the trick:
declare #curDate datetime, #maxDate datetime
declare #count tinyint
select #curDate = convert(datetime, '20120101', 112), #maxDate = getdate()
select #count = 0
while #curDate < #maxDate
begin
select #count = count(1) from tablename where Category = 'X' and convert(varchar(8), Date, 112) = convert(varchar(8), #curDate, 112)
if #count > 0
begin
insert into tablename
select 'X', #curDate, 0
end
select #curDate = dateadd(dd, 1, #curDate)
end