I'm trying to write a function to return the number of working days between 2 dates. I have a table with all the bank holidays in which has two fields: BHID and BHDate.
I've written the following SELECT query to test out my theory and it works nicely:
DECLARE #StartDate AS Date, #EndDate AS Date
SET #StartDate = '2015-01-01'
SET #EndDate = '2015-07-01'
;WITH CTE AS (
SELECT #StartDate AS StartDate, #EndDate AS EndDate, #StartDate AS DateCalc
UNION ALL
SELECT #StartDate AS StartDate, #EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
WHERE DATEADD(dd, 1, DateCalc) <= EndDate)
SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S'
option (Maxrecursion 0);
But when I try and put it in a function I get some errors around the SET line, it could be me just not being quite as good on functions as the rest of my knowledge or I was wondering whether it was to do with the CTE not having been used ahead of the SET line? Here's my attempt:
CREATE FUNCTION [dbo].[WorkingDays]
(#StartDate AS Date, #EndDate AS Date
)
RETURNS INT
AS
BEGIN
DECLARE #Days AS INT
;WITH CTE AS (
SELECT #StartDate AS StartDate, #EndDate AS EndDate, #StartDate AS DateCalc
UNION ALL
SELECT #StartDate AS StartDate, #EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
WHERE DATEADD(dd, 1, DateCalc) <= EndDate)
SET #Days =
(SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S'
option (Maxrecursion 0);)
RETURN #Days
END
I even tried inserting the result of the INT into a temporary table and then dropping this after setting #Days before then returning #Days but this then results in an error regarding not being allowed to produce temporary tables as part of a function.
Any help would be great, I'm sure it's a small thing but just escaping me currently.
Because a CTE can only be used in a query, rather than using SET to set the variable, use SELECT instead:
SELECT #Days =
(SELECT COUNT(*) AS Days
FROM CTE …
From MSDN:
A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE
statement that references some or all the CTE columns.
Worked it out with stuartd's help:
CREATE FUNCTION [dbo].[WorkingDays]
(#StartDate AS Date, #EndDate AS Date
)
RETURNS INT
AS
BEGIN
DECLARE #Days AS INT
;WITH CTE AS (
SELECT #StartDate AS StartDate, #EndDate AS EndDate, #StartDate AS DateCalc
UNION ALL
SELECT #StartDate AS StartDate, #EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
WHERE DATEADD(dd, 1, DateCalc) <= EndDate)
SELECT #Days =
(SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S')
option (Maxrecursion 0);
RETURN #Days
END
Thanks All
Use this function
CREATE FUNCTION [dbo].[WorkingDays]
(#Start_Date AS Date, #end_Date AS Date
)
RETURNS INT
AS
BEGIN
DECLARE #Days AS INT
WITH AllDays
AS ( SELECT #start_date AS [Date], 1 AS [level]
UNION ALL
SELECT DATEADD(DAY, 1, [Date]), [level] + 1
FROM AllDays
WHERE [Date] < #end_date )
SELECT #Days =COUNT(*) AS Days
FROM AllDays
LEFT JOIN psd.dbo.LUBankHolidays BH
ON AllDays.Date= BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, AllDays.Date) ,1) <> 'S'
RETURN #Days
END
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.
I have this query developed with the help of a guy here and I am not able to use this in function.
There is some sort of Syntax issue.
Here is the query
WITH CTE AS (
SELECT #STARTDATE AS STARTDATE
UNION ALL
SELECT DATEADD(D,1,STARTDATE)
FROM CTE
WHERE STARTDATE <#ENDDATE
),
WORKINGDAYS AS (
SELECT STARTDATE,
DATENAME(DW,STARTDATE)WEEKDAYS,
C1.CalanderDayName AS isweekend
FROM CTE c
LEFT JOIN HRM.tbl_Calendar C1 ON DATENAME(DW,STARTDATE) = C1.CalanderDayName
AND C1.IsOffDay = 1
)
SELECT COUNT(WEEKDAYS)as WORKINGDAYS
FROM WORKINGDAYS
WHERE isweekend IS NULL;
I want to create a function named fnGetWorkingDays
ALTER FUNCTION [dbo].[fnGetWorkingDays] (#StartDate datetime, #EndDate datetime)
RETURNS int
AS
BEGIN
DECLARE #dateFrom datetime
DECLARE #dateTo datetime
SET #dateFrom = #StartDate
SET #dateTo = #EndDate
DECLARE #WORKDAYS INT
SELECT #WORKDAYS =
;WITH CTE AS (
SELECT #STARTDATE AS STARTDATE
UNION ALL
select DATEADD(D,1,STARTDATE)
FROM CTE
WHERE STARTDATE <#ENDDATE
)
,WORKINGDAYS AS (
SELECT STARTDATE,DATENAME(DW,STARTDATE)WEEKDAYS, C1.CalanderDayName AS isweekend
FROM CTE c
LEFT JOIN HRM.tbl_Calendar C1 ON DATENAME(DW,STARTDATE)=C1.CalanderDayName AND C1.IsOffDay=1
)
SELECT COUNT(WEEKDAYS)as WORKINGDAYS FROM WORKINGDAYS WHERE isweekend is null
RETURN #WORKDAYS
END
Try With this Below function .let me know back for any errors
CREATE FUNCTION [dbo].[fnGetWorkingDays] (#StartDate datetime, #EndDate datetime)
RETURNS int
AS
BEGIN
DECLARE #WORKDAYS INT
;WITH CTE AS (
SELECT #STARTDATE AS STARTDATE
UNION ALL
SELECT DATEADD(D,1,STARTDATE)
FROM CTE
WHERE STARTDATE <#ENDDATE
),
WORKINGDAYS AS (
SELECT STARTDATE,
DATENAME(DW,STARTDATE)WEEKDAYS,
C1.CalanderDayName AS isweekend
FROM CTE c
LEFT JOIN HRM.tbl_Calendar C1 ON DATENAME(DW,STARTDATE) = C1.CalanderDayName
AND C1.IsOffDay = 1
)
SELECT #WORKDAYS=COUNT(WEEKDAYS) FROM WORKINGDAYS WHERE isweekend is null
RETURN #WORKDAYS
END
You are getting the Error because your Assignment operation for the Variable #WORKDAYS is Wrong. Change it Like This
ALTER FUNCTION [dbo].[fnGetWorkingDays]
(
#StartDate DATETIME,
#EndDate DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE #dateFrom DATETIME;
DECLARE #dateTo DATETIME;
SET #dateFrom = #StartDate;
SET #dateTo = #EndDate;
DECLARE #WORKDAYS INT;
WITH CTE
AS
(
SELECT
#STARTDATE AS STARTDATE
UNION ALL
SELECT
DATEADD(D, 1, STARTDATE)
FROM CTE
WHERE STARTDATE < #ENDDATE
),WORKINGDAYS
AS
(
SELECT
DATENAME(DW, STARTDATE) WEEKDAYS,
C1.CalanderDayName AS isweekend
FROM CTE c
LEFT JOIN HRM.tbl_Calendar C1
ON DATENAME(DW, STARTDATE) = C1.CalanderDayName
AND C1.IsOffDay = 1
)
SELECT
#WORKDAYS = COUNT(WEEKDAYS)--Asign Variable Here
FROM WORKINGDAYS
WHERE isweekend IS NULL;
RETURN #WORKDAYS;
END;
You have written wrong syntax to set #WORKDAYS, try below syntax
CREATE FUNCTION [dbo].[fnGetWorkingDays] (#StartDate datetime, #EndDate datetime)
RETURNS int
AS
BEGIN
DECLARE #WORKDAYS INT
;WITH CTE AS (
SELECT #STARTDATE AS STARTDATE
UNION ALL
select DATEADD(D,1,STARTDATE)
FROM CTE
WHERE STARTDATE <#ENDDATE
)
,WORKINGDAYS AS (
SELECT STARTDATE,DATENAME(DW,STARTDATE)WEEKDAYS, C1.CalanderDayName AS isweekend
FROM CTE c
LEFT JOIN HRM.tbl_Calendar C1 ON DATENAME(DW,STARTDATE)=C1.CalanderDayName AND C1.IsOffDay=1
)
SELECT #WORKDAYS=COUNT(WEEKDAYS) FROM WORKINGDAYS WHERE isweekend is null
RETURN #WORKDAYS
END
Here i'm Trying to create stored procedure.. group by detepart..is it correct or wrong.??
CREATE PROCEDURE sp_NewvsOldOrders
(
#StartDate DATETIME,
#EndDate DATETIME
)
AS BEGIN
DECLARE #NewCount INT
, #NewRevenue INT
, #NewDate DATETIME
SELECT
#NewDate = DATEPART(DAY, od.OrderDate)
, #NewCount = COUNT(*)
, #NewRevenue = SUM((CONVERT(DECIMAL(18, 2), od.TotalAmount)))
FROM orderdetails od
INNER JOIN customer c
ON od.customerid = c.customerid
WHERE (CONVERT(DATETIME, (CONVERT(VARCHAR(50), od.orderdate, 101))) BETWEEN #StartDate AND #EndDate)
AND ((CONVERT(DATETIME, (CONVERT(VARCHAR(50), od.orderdate, 101)))) = (CONVERT(DATETIME, (CONVERT(VARCHAR(50), c.registereddate, 101)))))
AND (od.TransactionId IS NOT NULL)
AND (od.TransactionId <> '')
GROUP BY DATEPART(DAY, od.OrderDate)
, od.OrderDate
END
Perhaps this is how your stored procedures should be. As you didn't explain your requirement, i can really tell what you are trying to achieve
CREATE PROCEDURE sp_NewvsOldOrders
(
#StartDate DATETIME,
#EndDate DATETIME
)
AS BEGIN
-- not used
--DECLARE #NewCount INT
-- , #NewRevenue INT
-- , #NewDate DATETIME
SELECT
NewDate = DATEPART(DAY, od.OrderDate)
, NewCount = COUNT(*)
, NewRevenue = SUM((CONVERT(DECIMAL(18, 2), od.TotalAmount)))
FROM orderdetails od
INNER JOIN customer c
ON od.customerid = c.customerid
WHERE (od.orderdate >= StartDate)
AND (od.orderdate < DATEADD(DAY, 1, #EndDate))
AND (CONVERT(VARCHAR(50), od.orderdate, 101)
= CONVERT(DATETIME, c.registereddate, 101))
AND (od.TransactionId IS NOT NULL)
AND (od.TransactionId <> '')
GROUP BY DATEPART(DAY, od.OrderDate)
--, od.OrderDate
END
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
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