Related
Server: Microsoft SQL Server
SQLFiddle: http://www.sqlfiddle.com/#!18/cdfa3/1/0
If I have rows containing a "start" date and an "end date", how can write a SQL query that will list the days that are not contained between those dates.
Example (see SQLFiddle link above for a playable demo):
startdate enddate
2019-06-06 00:00:00.000 2019-06-08 00:00:00.000
2019-06-10 00:00:00.000 2019-06-11 00:00:00.000
2019-06-12 00:00:00.000 2019-06-13 00:00:00.000
We have a coverage gap on June 9th, because we have coverage from June 6th-June 8th, then on June 10th-June 13th.
How is it possible to identify the date of June 9th as having no coverage based on rows that have date ranges?
You could use generated calendar table and LEFT JOIN:
DECLARE #min DATE, #max DATE;
SELECT #min = MIN(workingdatestart), #max = MAX(workingdateend) FROM workingdates;
WITH cte AS (
SELECT DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY 1/0), #min) AS d
FROM sys.objects s, sys.objects s2
)
SELECT c.d AS gap
FROM cte c
LEFT JOIN workingdates w ON c.d BETWEEN w.workingdatestart and w.workingdateend
WHERE c.d < #max AND w.workingDateId IS NULL;
db<>fiddle demo
#Lukasz Szozda stole my thunder. My answer is similar but does not use variables (I'm not suggesting that's good or bad.. just calling it out).
You can create a calendar table function (see example below) then perform a LEFT ANTI SEMI JOIN against your working days table. The benefit to this solution is the calendar table generates 0 IO.
Solution:
WITH r(L,H) AS
(
SELECT CAST(MIN(w.workingdatestart) AS DATE), CAST(MAX(w.workingdateend) AS DATE)
FROM dbo.workingdates AS w
),
cal AS
(
SELECT c.Dt
FROM r
CROSS APPLY dbo.calendar(r.L,r.H) AS c
)
SELECT c.Dt
FROM cal AS c
EXCEPT
SELECT c.Dt
FROM cal AS c
JOIN dbo.workingdates AS w
ON c.Dt BETWEEN w.workingdatestart AND w.workingdateend;
.. and the function:
CREATE FUNCTION dbo.calendar(#startdate DATE, #enddate DATE)
RETURNS TABLE WITH SCHEMABINDING AS RETURN
WITH E1(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS x(x)),
iTally(N) AS
(
SELECT 0 UNION ALL
SELECT TOP (DATEDIFF(DAY,#startDate,#endDate)) ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM E1 a, E1 b, E1 c
)
SELECT sortKey = i.N, Dt = DATEADD(DAY, i.N, #startDate)
FROM iTally AS i;
To achieve this you need a table that contains all dates between your minimal and maximal dates. Then just filter rows that don't exist in workingdates using LEFT JOIN.
declare #minDate date = (select min([workingdatestart]) from [workingdates])
declare #maxDate date = (select max([workingdateend]) from [workingdates])
declare #Date date = #minDate
create table #rangeOfDates (dat date)
while #Date <= #maxDate
begin
insert into #rangeOfDates values (#Date)
set #Date = dateadd(day , 1, #Date)
end
select r.dat
from #rangeOfDates as r
left join workingdates as w
on r.dat between w.workingdatestart and w.workingdateend
where w.workingdateID is null
Result:
dat
2019-06-09
I'm trying to create a query to see if a record(Alert) was open during a specific time period.
The first table has only the current status of the record.
table: ALERT
intID |CurrentStateID| DateCreated
-----------------------------------
3 |Closed | 10/11/2009
the second table has the history of the status for the alert.
tblState
intContextID|strToStateName|datTimeStamp
-----------------------------------
3 |Unassigned |10/11/2009
3 |Closed |10/14/2009
Here is my desired output:
DESIRED OUTPUT
DATE |DAY |TOTAL_OPEN
-----------------------------------
10/10/2009 |Friday |0
10/11/2009 |Saturday |1
10/12/2009 |Sunday |1
10/13/2009 |Monday |1
10/14/2009 |Tuesday |0
10/15/2009 |Wednesday |0
I was able to write some of the code but I think its the join on the AllDays table that might be wrong.
DECLARE #StartDate DATETIME = '2009-10-10';
DECLARE #EndDate DATETIME = '2009-10-15 23:59:59';
WITH AllDays
AS (
SELECT #StartDate AS [Date]
, 1 AS [level]
UNION ALL
SELECT DATEADD(DAY, 1, [Date])
, [level] + 1
FROM AllDays
WHERE [Date] < #EndDate
)
SELECT CAST(AllDays.[Date] AS DATE) AS 'DATE'
, datename(dw, AllDays.[Date]) AS 'DAY'
,ISNULL(TOTAL_OPEN, 0) as TOTAL_OPEN
FROM AllDays
LEFT JOIN (
SELECT DISTINCT s.datTimeStamp AS 'DATE'
, count(A.intID) AS 'TOTAL_OPEN'
FROM Alert A
INNER JOIN tblState S ON A.intID = S.intContextID
WHERE strToStateName = 'Unassigned'
GROUP BY datTimeStamp
) AS TOTAL_OPEN ON TOTAL_OPEN.DATE = AllDays.[Date]
The Alert was open from 10-11 to 10-13 but since I'm joining on datetimestamp the results only show 1 for 10/11.
Here's the schema a link!
OK.. one (admittedly hideous) way to do this to create a udf that calculates the open count for a given day like so:
CREATE FUNCTION [dbo].[fn_open_alert_count] (#date date)
RETURNS int
AS
BEGIN
RETURN (SELECT COUNT(intContextID) as opencount FROM tblState WHERE strToStateName = 'Unassigned' AND datTimeStamp <= #date) - (SELECT COUNT(intContextID) as closedcount FROM tblState WHERE strToStateName = 'Closed' AND datTimeStamp <= #date)
END
Then you can do the query like so:
DECLARE #StartDate DATETIME = '2009-10-10';
DECLARE #EndDate DATETIME = '2009-10-15 23:59:59';
WITH AllDays
AS (
SELECT #StartDate AS [Date]
, 1 AS [level]
UNION ALL
SELECT DATEADD(DAY, 1, [Date])
, [level] + 1
FROM AllDays
WHERE [Date] < #EndDate
)
SELECT CAST(AllDays.[Date] AS DATE) AS 'DATE'
, datename(dw, AllDays.[Date]) AS 'DAY'
,[dbo].[fn_open_alert_count](AllDays.[Date])
FROM AllDays
If you don't have permission to create functions you can inline the functionality like so:
DECLARE #StartDate DATETIME = '2009-10-10';
DECLARE #EndDate DATETIME = '2009-10-15 23:59:59';
WITH AllDays
AS (
SELECT #StartDate AS [Date]
, 1 AS [level]
UNION ALL
SELECT DATEADD(DAY, 1, [Date])
, [level] + 1
FROM AllDays
WHERE [Date] < #EndDate
)
SELECT CAST(AllDays.[Date] AS DATE) AS 'DATE'
, datename(dw, AllDays.[Date]) AS 'DAY'
,(SELECT COUNT(intContextID) as opencount FROM tblState WHERE strToStateName = 'Unassigned' AND datTimeStamp <= AllDays.[Date]) - (SELECT COUNT(intContextID) as closedcount FROM tblState WHERE strToStateName = 'Closed' AND datTimeStamp <= AllDays.[Date]) AS TOTAL_OPEN
FROM AllDays
This gives me the following output:
DATE DAY TOTAL_OPEN
2009-10-10 Saturday 0
2009-10-11 Sunday 1
2009-10-12 Monday 1
2009-10-13 Tuesday 1
2009-10-14 Wednesday 0
2009-10-15 Thursday 0
2009-10-16 Friday 0
Lets say I have following query:
SELECT top (5) CAST(Created AS DATE) as DateField,
Count(id) as Counted
FROM Table
GROUP BY CAST(Created AS DATE)
order by DateField desc
Lets say it will return following data set
DateField Counted
2016-01-18 34
2016-01-17 99
2016-01-14 1
2015-12-28 1
2015-12-27 6
But when I have Counted = 0 for certain Date I would like to get that in result set. So for example it should look like following
DateField Counted
2016-01-18 34
2016-01-17 99
2016-01-16 0
2016-01-15 0
2016-01-14 1
Thank you!
Expanding upon KM's answer, you need a date table which is like a numbers table.
There are many examples on the web but here's a simple one.
CREATE TABLE DateList (
DateValue DATE,
CONSTRAINT PK_DateList PRIMARY KEY CLUSTERED (DateValue)
)
GO
-- Insert dates from 01/01/2015 and 12/31/2015
DECLARE #StartDate DATE = '01/01/2015'
DECLARE #EndDatePlus1 DATE = '01/01/2016'
DECLARE #CurrentDate DATE = #StartDate
WHILE #EndDatePlus1 > #CurrentDate
BEGIN
INSERT INTO DateList VALUES (#CurrentDate)
SET #CurrentDate = DATEADD(dd,1,#CurrentDate)
END
Now you have a table
then you can rewrite your query as follows:
SELECT top (5) DateValue, isnull(Count(id),0) as Counted
FROM DateList
LEFT OUTER JOIN Table
on DateValue = CAST(Created AS DATE)
GROUP BY DateValue
order by DateValue desc
Two notes:
You'll need a where clause to specify your range.
A join on a cast isn't ideal. The type in your date table should match the type in your regular table.
One more solution as a single query:
;WITH dates AS
(
SELECT CAST(DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY [object_id]) - 1, '2016-01-14') as date) 'date'
FROM sys.all_objects
)
SELECT TOP 5
[date] AS 'DateField',
SUM(CASE WHEN Created IS NULL THEN 0 ELSE 1 END) AS 'Counted'
FROM dates
LEFT JOIN Table ON [date]=CAST(Created as date)
GROUP BY [date]
ORDER BY [date]
For a more edgy solution, you could use a recursive common table expression to create the date list. PLEASE NOTE: do not use recursive common table expressions in your day job! They are dangerous because it is easy to create one that never terminates.
DECLARE #StartDate date = '1/1/2016';
DECLARE #EndDate date = '1/15/2016';
WITH DateList(DateValue)
AS
(
SELECT DATEADD(DAY, 1, #StartDate)
UNION ALL
SELECT DATEADD(DAY, 1, DateValue)
FROM DateList
WHERE DateList.DateValue < #EndDate
)
SELECT DateValue, isnull(Count(id),0) as Counted
FROM DateList
LEFT OUTER JOIN [Table]
ON DateValue = CAST(Created AS DATE)
GROUP BY DateValue
ORDER BY DateValue DESC
The following query gives playing time of the users from the database on daily basis for the last 15 days. It adds 0 if no game is played. Now I want to get the data of playing time on weekly basis and 0 if no game is played in the whole week. So I want the query to give the last 15 weeks of data.
Here is the daily query.
CREATE PROCEDURE [dbo].[spGetPlayingTimeOfthepeoplesPerDay] #email NVARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #MinDate DATE
,#MaxDate DATE
,#LastXDays INT
SELECT #LastXDays = - 15
SELECT #MaxDate = peoples.l_update
FROM peoples
WHERE peoples.email = #email
DECLARE #test TABLE (
quantity VARCHAR(100)
,DATE DATE
,TimePerDay DECIMAL(5, 2)
);
WITH CTE
AS (
SELECT peoples.email
,peoples.l_update
,act.quantity
,act.starttime
,act.endtime
,act.duration AS [Totaltime]
FROM peoples
INNER JOIN MPeoples ON peoples.Id = MPeoples.parent_id
INNER JOIN slines ON MPeoples.id = slines.movesuser_id
INNER JOIN seg ON slines.id = seg.sline_id
INNER JOIN act ON seg.id = act.seg_id
WHERE act.quantity = 'playing'
AND (peoples.email = #email)
GROUP BY peoples.email
,act.quantity
,act.duration
,act.starttime
,act.endtime
,peoples.l_update
)
INSERT INTO #test (
quantity
,DATE
,TimePerDay
)
SELECT quantity
,Cast(starttime AS DATE) AS DATE
,SUM(datediff(second, starttime, endtime)) / 60.0 AS TimePerDay
FROM cte WITH (NOLOCK)
WHERE starttime >= dateadd(day, #LastXDays, l_update)
GROUP BY quantity
,cast(starttime AS DATE)
SELECT #MaxDate = #MaxDate
,#MinDate = dateadd(day, (#LastXDays + 1), #MaxDate);
WITH AllDates
AS (
SELECT #MinDate AS xDate
UNION ALL
SELECT Dateadd(Day, 1, xDate)
FROM AllDates AS ad
WHERE ad.xDate < #MaxDate
)
SELECT 'playing' AS quantity
,ad.xDate
,Isnull(t.TimePerDay, 0) AS TimePerDay
FROM AllDates AS ad WITH (NOLOCK)
LEFT JOIN #test AS t ON ad.xDate = t.DATE
END
Change the DATEADD from day to week. Hence, two changes:
dateadd(week, #LastXDays, l_update)
and
dateadd(week, (#LastXDays + 1), #MaxDate)
In this case, I would also rename the #LastXDays variable to #LastXWeeks.
CREATE PROCEDURE [dbo].[spGetPlayingTimeOfthepeoplesPerDay] #email NVARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #MinDate DATE
,#MaxDate DATE
,#LastXDays INT
SELECT #LastXWeeks = - 15
SELECT #MaxDate = peoples.l_update
FROM peoples
WHERE peoples.email = #email
DECLARE #test TABLE (
quantity VARCHAR(100)
,DATE DATE
,TimePerDay DECIMAL(5, 2)
);
WITH CTE
AS (
SELECT peoples.email
,peoples.l_update
,act.quantity
,act.starttime
,act.endtime
,act.duration AS [Totaltime]
FROM peoples
INNER JOIN MPeoples ON peoples.Id = MPeoples.parent_id
INNER JOIN slines ON MPeoples.id = slines.movesuser_id
INNER JOIN seg ON slines.id = seg.sline_id
INNER JOIN act ON seg.id = act.seg_id
WHERE act.quantity = 'playing'
AND (peoples.email = #email)
GROUP BY peoples.email
,act.quantity
,act.duration
,act.starttime
,act.endtime
,peoples.l_update
)
INSERT INTO #test (
quantity
,DATE
,TimePerDay
)
SELECT quantity
,Cast(starttime AS DATE) AS DATE
,SUM(datediff(second, starttime, endtime)) / 60.0 AS TimePerDay
FROM cte WITH (NOLOCK)
WHERE starttime >= dateadd(week, #LastXWeeks, l_update)
GROUP BY quantity
,cast(starttime AS DATE)
SELECT #MaxDate = #MaxDate
,#MinDate = dateadd(week, (#LastXWeeks + 1), #MaxDate);
WITH AllDates
AS (
SELECT #MinDate AS xDate
UNION ALL
SELECT Dateadd(Day, 7, xDate)
FROM AllDates AS ad
WHERE ad.xDate < #MaxDate
)
SELECT 'playing' AS quantity
,ad.xDate
,Isnull(t.TimePerDay, 0) AS TimePerDay
FROM AllDates AS ad WITH (NOLOCK)
LEFT JOIN #test AS t ON ad.xDate = t.DATE
END
Also, a piece of advice: don't use query hints (NOLOCK) if you do not understand their use. In this case, using NOLOCK can have disastrous effects on your results.
Here are a few articles which you should read before deciding if you are going to keep using NOLOCK or not.
Understanding the SQL Server NOLOCK hint
Bad habits : Putting NOLOCK everywhere
I have an app that needs to show a bar graph for activity over the last 30 days. The graph needs to show all days even if there is no activity for the day.
for example:
DATE COUNT
==================
1/1/2011 5
1/2/2011 3
1/3/2011 0
1/4/2011 4
1/5/2011 0
etc....
I could do post processing after the query to figure out what dates are missing and add them but was wondering if there is an easier way to do it in SQL Server. Thanks much
You can use a recursive CTE to build your list of 30 days, then join that to your data
--test
select cast('05 jan 2011' as datetime) as DT, 1 as val into #t
union all select CAST('05 jan 2011' as datetime), 1
union all select CAST('29 jan 2011' as datetime), 1
declare #start datetime = '01 jan 2011'
declare #end datetime = dateadd(day, 29, #start)
;with amonth(day) as
(
select #start as day
union all
select day + 1
from amonth
where day < #end
)
select amonth.day, count(val)
from amonth
left join #t on #t.DT = amonth.day
group by amonth.day
>>
2011-01-04 00:00:00.000 0
2011-01-05 00:00:00.000 2
2011-01-06 00:00:00.000 0
2011-01-07 00:00:00.000 0
2011-01-08 00:00:00.000 0
2011-01-09 00:00:00.000 0
...
Using CTE:
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('20110201' as Date)
)
SELECT dt.[DATE], ISNULL(md.[COUNT], 0) as [COUNT]
FROM [DateTable] dt
LEFT JOIN [MyData] md
ON md.[DATE] = dt.[DATE]
This is assuming everything's a Date; if it's DateTime, you'll have to truncate (with DATEADD(dd, 0, DATEDIFF(dd, 0, [DATE]))).
#Alex K.'s answer is completely correct, but it doesn't work for versions that do not support Recursive common table expressions (like the version I'm working with). In this case the following would do the job.
DECLARE #StartDate datetime = '2015-01-01'
DECLARE #EndDate datetime = SYSDATETIME()
;WITH days AS
(
SELECT DATEADD(DAY, n, DATEADD(DAY, DATEDIFF(DAY, 0, #StartDate), 0)) as d
FROM ( SELECT TOP (DATEDIFF(DAY, #StartDate, #EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
select days.d, count(t.val)
FROM days LEFT OUTER JOIN yourTable as t
ON t.dateColumn >= days.d AND t.dateColumn < DATEADD(DAY, 1, days.d)
GROUP BY days.d
ORDER BY days.d;
My scenario was a bit more complex than the OP example, so thought I'd share to help others who have similar issues. I needed to group sales orders by date taken, whereas the orders are stored with datetime.
So in the "days" lookup table I could not really store as a date time with the time being '00:00:00.000' and get any matches. Therefore I stored as a string and I tried to join on the converted value directly.
That did not return any zero rows, and the solution was to do a sub-query returning the date already converted to a string.
Sample code as follows:
declare #startDate datetime = convert(datetime,'09/02/2016')
declare #curDate datetime = #startDate
declare #endDate datetime = convert(datetime,'09/09/2016')
declare #dtFormat int = 102;
DECLARE #null_Date varchar(24) = '1970-01-01 00:00:00.000'
/* Initialize #days table */
select CONVERT(VARCHAR(24),#curDate, #dtFormat) as [Period] into #days
/* Populate dates into #days table */
while (#curDate < #endDate )
begin
set #curDate = dateadd(d, 1, #curDate)
insert into #days values (CONVERT(VARCHAR(24),#curDate, #dtFormat))
end
/* Outer aggregation query to group by order numbers */
select [Period], count(c)-case when sum(c)=0 then 1 else 0 end as [Orders],
sum(c) as [Lines] from
(
/* Inner aggregation query to sum by order lines */
select
[Period], sol.t_orno, count(*)-1 as c
from (
/* Inner query against source table with date converted */
select convert(varchar(24),t_dldt, #dtFormat) as [shipdt], t_orno
from salesorderlines where t_dldt > #startDate
) sol
right join #days on shipdt = #days.[Period]
group by [Period], sol.t_orno
) as t
group by Period
order by Period desc
drop table #days
Sample Results:
Period Orders Lines
2016.09.09 388 422
2016.09.08 169 229
2016.09.07 1 1
2016.09.06 0 0
2016.09.05 0 0
2016.09.04 165 241
2016.09.03 0 0
2016.09.02 0 0
Either define a static table containing dates or create a temp table \ table variable on the fly to store each date between (and including) the min and max dates in the activity table you're working with.
Use an outer join between the two tables to make sure that each date in your dates table is reflected in the output.
If you use a static dates table you will likely want to limit the date range that is output to only the range needed in the graph.
Without Transact-SQL: MS SQL 2005 - Get a list of all days of a Month:
In my case '20121201' is a predefined value.
SELECT TOp (Select Day(DateAdd(day, -Day(DateAdd(month, 1,
'20121201')),
DateAdd(month, 1, '20121201')))) DayDate FROM ( SELECT DATEADD(DAY,ROW_NUMBER() OVER (ORDER BY (SELECT
NULL))-1,'20121201') as DayDate FROM sys.objects s1 CROSS JOIN
sys.objects s2 ) q
Recursive CTE works for max 80 years which is good enough:
DECLARE #dStart DATE,
#dEnd DATE
SET #dStart = GETDATE ()
SET #dEnd = DATEADD (YEAR, 80, #dStart)
;WITH CTE AS
(
SELECT #dStart AS dDay
UNION ALL
SELECT DATEADD (DAY, 1, dDay)
FROM CTE
WHERE dDay < #dEnd
)
SELECT * FROM CTE
OPTION (MaxRecursion 32767)
create a numbers table and use it like:
declare #DataTable table (DateColumn datetime)
insert #DataTable values ('2011-01-09')
insert #DataTable values ('2011-01-10')
insert #DataTable values ('2011-01-10')
insert #DataTable values ('2011-01-11')
insert #DataTable values ('2011-01-11')
insert #DataTable values ('2011-01-11')
declare #StartDate datetime
SET #StartDate='1/1/2011'
select
#StartDate+Number,SUM(CASE WHEN DateColumn IS NULL THEN 0 ELSE 1 END)
FROM Numbers
LEFT OUTER JOIN #DataTable ON DateColumn=#StartDate+Number
WHERE Number>=1 AND Number<=15
GROUP BY #StartDate+Number
OUTPUT:
----------------------- -----------
2011-01-02 00:00:00.000 0
2011-01-03 00:00:00.000 0
2011-01-04 00:00:00.000 0
2011-01-05 00:00:00.000 0
2011-01-06 00:00:00.000 0
2011-01-07 00:00:00.000 0
2011-01-08 00:00:00.000 0
2011-01-09 00:00:00.000 1
2011-01-10 00:00:00.000 2
2011-01-11 00:00:00.000 3
2011-01-12 00:00:00.000 0
2011-01-13 00:00:00.000 0
2011-01-14 00:00:00.000 0
2011-01-15 00:00:00.000 0
2011-01-16 00:00:00.000 0
(15 row(s) affected)
Maybe something like this:
Create DaysTable countaining the 30 days.
And DataTable containing "day" column and "count" column.
And then left join them.
WITH DaysTable (name) AS (
SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 -- .. And so on to 30
),
DataTable (name, value) AS (
SELECT DATEPART(DAY, [Date]), [Count]
FROM YourExampleTable
WHERE [Date] < DATEADD (day , -30 , getdate())
)
SELECT DaysTable.name, DataTable.value
FROM DaysTable LEFT JOIN
DataTable ON DaysTable.name = DataTable.name
ORDER BY DaysTable.name
For those with a recursion allergy
select SubQ.TheDate
from
(
select DATEADD(day, a.a + (10 * b.a) + (100 * c.a), DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0) - 30) AS TheDate
from
(
(select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
)
WHERE a.a + (10 * b.a) + (100 * c.a) < 30
) AS SubQ
ORDER BY TheDate
Try it.
DECLARE #currentDate DATETIME = CONVERT(DATE, GetDate())
DECLARE #startDate DATETIME = DATEADD(DAY, -DAY(#currentDate)+1, #currentDate)
;WITH fnDateNow(DayOfDate) AS
(
SELECT #startDate AS DayOfDate
UNION ALL
SELECT DayOfDate + 1 FROM fnDateNow WHERE DayOfDate < #currentDate
) SELECT fnDateNow.DayOfDate FROM fnDateNow
DECLARE #StartDate DATE = '20110101', #NumberOfYears INT = 1;
DECLARE #CutoffDate DATE = DATEADD(YEAR, #NumberOfYears, #StartDate);
CREATE TABLE Calender
(
[date] DATE
);
INSERT Calender([date])
SELECT d
FROM
(
SELECT d = DATEADD(DAY, rn - 1, #StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, '2011-01-01', '2011-12-31'))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id]
) AS x
) AS y;
create table test(a date)
insert into test values('1/1/2011')
insert into test values('1/1/2011')
insert into test values('1/1/2011')
insert into test values('1/1/2011')
insert into test values('1/1/2011')
insert into test values('1/2/2011')
insert into test values('1/2/2011')
insert into test values('1/2/2011')
insert into test values('1/4/2011')
insert into test values('1/4/2011')
insert into test values('1/4/2011')
insert into test values('1/4/2011')
select c.date as DATE,count(t.a) as COUNT from calender c left join test t on c.date = t.a group by c.date