How to show all days recorded - sql-server

I have a table in Sql Server named Lab_Analysis(ID int,Date Date,HeatNo decimal(18,3),Mn decimal(18,3),CaO decimal(18,3))
Now if i want to View Sum of HeatNo, Mn, CaO then i will have to enter Month and Year. Suppose i give month = 2(means February) then it should show sum of HeatNo, Mn, CaO of each days of that Month and if on any days there is no Record found then it should 0. For this I did following:
Declare #D table(Dt int,Val decimal(18,3))
insert into #D values(1,0),(2,0),(3,0),(4,0),(5,0),(6,0),(7,0),(8,0),(9,0),
(10,0),(11,0),(12,0),(13,0),(14,0),(15,0),(16,0),(17,0),(18,0),(19,0),
(20,0),(21,0),(22,0),(23,0),(24,0),(25,0),(26,0),(27,0),(28,0),(29,0),(30,0),(31,0)
SELECT D.Dt,ISNULL(SUM(L.HeatNo),0) HeatNo FROM #D D LEFT JOIN Lab_Analysis
L ON DATEPART(dd,L.Date)=D.Dt where DATEPART(MM,L.Date)=2 GROUP BY D.Dt
Here in the February month Records are inserted only on 11 and 13 February. The above query is showing only rows but it shows all days records. if no record found on any days then it should show 0 which is not showing by the above query. How to solve this?

You are using the L.Date in your where clause DATEPART(MM,L.Date)=2, that converts your LEFT JOIN into an INNER JOIN, you need to add this condition in the ON clause
Declare #D table(Dt int,Val decimal(18,3))
insert into #D values(1,0),(2,0),(3,0),(4,0),(5,0),(6,0),(7,0),(8,0),(9,0),
(10,0),(11,0),(12,0),(13,0),(14,0),(15,0),(16,0),(17,0),(18,0),(19,0),
(20,0),(21,0),(22,0),(23,0),(24,0),(25,0),(26,0),(27,0),(28,0),(29,0),(30,0),(31,0)
SELECT D.Dt,ISNULL(SUM(L.HeatNo),0) HeatNo
FROM #D D
LEFT JOIN Lab_Analysis L ON
DATEPART(dd,L.Date)=D.Dt
AND DATEPART(MM,L.Date)=2
GROUP BY D.Dt
Instead of create that set of values for each month, I recomend you to use a CTE to create your dates
DECLARE
#Year INT,
#Month INT,
#start DATE,
#end DATE
SET #Year = 2019
SET #Month = 2
SET #start = DATEFROMPARTS(#Year, #Month, 1)
SET #end = DATEADD(day, -1,DATEADD(month, 1, #start))
;WITH dates AS (
SELECT #start as theDate
UNION ALL
SELECT DATEADD(day, 1, theDate)
FROM dates
WHERE DATEADD(day, 1, theDate) <= #end
)
SELECT D.theDate,ISNULL(SUM(L.HeatNo),0) HeatNo
FROM dates D
LEFT JOIN Lab_Analysis L ON L.Date = theDate
GROUP BY theDate

Related

Determine Days Not Covered Based on Fields Containing "From" and "To" Dates

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

Stored procedure to accept starting date and number of consecutive dates beginning from start date

The parameter should consider, A starting date and the number of the consecutive dates beginning with the starting date.
The stored procedure then should populate all columns of the DateRange table according to the two provided parameters.
I created a table :
CREATE TABLE DateRange
(
DateID INT IDENTITY,
DateValue DATE,
Year INT,
Quarter INT,
Month INT,
DayOfWeek INT
);
Stored procedure code:
CREATE FUNCTION dbo.DateRange_sp4
(#StartDate DATE,
#NumberofConsecutivedays INT)
RETURNS #DateList TABLE
(
DateID INT,
DateValue DATE,
Year INT,
Quarter INT,
Month INT,
DayOfWeek INT
)
AS
BEGIN
DECLARE #Counter INT = 0;
WHILE (#Counter < #NumberofConsecutivedays)
BEGIN
INSERT INTO #DateList
VALUES (#Counter + 1,
DATEADD(DAY, #Counter, #StartDate),
DATEPART(YEAR, #StartDate),
DATEPART(QUARTER, #StartDate),
DATEPART(MONTH, #StartDate),
DatePart(WEEKDAY, #StartDate) );
SET #Counter += 1
END
RETURN;
END
GO
SELECT *
FROM dbo.DateRange_sp4('2018-07-13', 20);
My output returns the same result for year, quarter, month and dayofweek. How to split the date in different columns? Or is there any other way to do it?
Use a tally table... it'll be A LOT faster. Check it out for 10K days... and run your loop code for 10K days.
declare #dateparameter date = '1900-04-12'
declare #numOfDays int = 10000
;WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select
datevalue = #dateparameter
,year = datepart(year,#dateparameter)
,quarter = datepart(quarter,#dateparameter)
,month = datepart(month,#dateparameter)
,dayofweek = datepart(weekday,#dateparameter)
union all
select
datevalue = dateadd(day,N,#dateparameter)
,year = datepart(year,dateadd(day,N,#dateparameter))
,quarter = datepart(quarter,dateadd(day,N,#dateparameter))
,month = datepart(month,dateadd(day,N,#dateparameter))
,dayofweek = datepart(weekday,dateadd(day,N,#dateparameter))
from cteTally
where N <= #numOfDays
But, if you are going to reference this a lot, why not make a persisted table? Aaron Bertran has a great article on this:
https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/
You're using the same value for #StartDate each time you iterate through the loop.
Assuming you want the date parts of each date in your sample output, at the end of your loop you should update the value of #StartDate.
INSERT INTO #DateList
VALUES(#Counter + 1, DATEADD(day,#Counter, #StartDate), DATEPART(year, #StartDate), DATEPART(QUARTER, #StartDate),DATEPART(month, #StartDate), DatePart(WEEKDAY,#StartDate)) ;
SET #StartDate = DATEADD(day,#Counter + 1, #StartDate);
SET #Counter +=1;

TSQL get last day of previous months upto a specified month

I need to get last day of all previous months including current month, upto a specified month. For example, I need last days of september, aug, july, june, may, april, march, feb, jan, dec 2015 like so:
temptable_mytable:
last_day_of_month
-----------------
2016-09-30
2016-08-31
2016-07-31
2016-06-30
2016-05-31
2016-04-30
2016-03-31
2016-02-30
2016-01-31
2015-12-31
I need to specify the month and year to go back to - in above case it's December 2015, but it could also be September 2015 and such. Is there a way that I can do a loop and do this instead of having to calculate separately for each month end?
Use a recursive CTE with the EOMONTH function.
DECLARE #startdate DATE = '2016-01-01'
;WITH CTE
AS
(
SELECT EOMONTH(GETDATE()) as 'Dates'
UNION ALL
SELECT EOMONTH(DATEADD(MONTH, -1, [Dates]))
FROM CTE WHERE Dates > DATEADD(MONTH, 1, #startdate)
)
SELECT * FROM CTE
with temp as (select -1 i union all
select i+1 i from temp where i < 8)
select DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+i*-1,0)) from temp
declare #LASTMONTH date = '2018-10-01';
WITH MTHS AS (
SELECT dateadd(month,month(getdate()),dateadd(year,year(getdate()) - 1900, 0)) aday
UNION ALL
SELECT DATEADD(month,1,aday) from MTHS WHERE aday <= #LASTMONTH
),
LASTDAYS AS (SELECT DATEADD(day,-1,aday) finaldayofmonth from MTHS)
select * from LASTDAYS
Here is a version that goes forward or backwards as appropriate
declare #LASTMONTH date = '2013-10-01';
WITH DIF AS (SELECT CASE WHEN
YEAR(#LASTMONTH) * 12 + MONTH(#LASTMONTH)
>= YEAR(GETDATE()) * 12 + MONTH(getdate()) THEN 1 ELSE -1 END x),
MTHS AS (
SELECT dateadd(month,month(getdate()),dateadd(year,year(getdate()) - 1900, 0)) aday
UNION ALL
SELECT DATEADD(month,(SELECT X from dif),aday) from MTHS
WHERE month(aday) != month(dateadd(month,1,#LASTMONTH)) or YEAR(aday) != YEAR(dateadd(month,1,#LASTMONTH))
),
LASTDAYS AS (SELECT DATEADD(day,-1,aday) finaldayofmonth from MTHS)
select * from LASTDAYS order by finaldayofmonth
Here's one approach, using a CTE to generate a list of incrementing numbers to allow us to then have something to select from and use in a DATEADD to go back for the appropriate number of months.
Typically, if you're doing this quite frequently, instead of generating numbers on the fly like this with the CROSS JOIN, I'd recommend just creating a "Numbers" table that just holds numbers from 1 to "some number high enough to meet your needs"
DECLARE #Date DATE = '20151201'
DECLARE #MonthsBackToGo INTEGER
SELECT #MonthsBackToGo = DATEDIFF(mm, #Date, GETDATE()) + 1;
WITH _Numbers AS
(
SELECT TOP (#MonthsBackToGo) ROW_NUMBER() OVER (ORDER BY o.object_id) AS Number
FROM sys.objects o
CROSS JOIN sys.objects o2
)
SELECT EOMONTH(DATEADD(mm, -(Number- 1), GETDATE())) AS last_day_of_month
FROM _Numbers
This should scale out no matter how far you go back or forward for your originating table or object.
SET NOCOUNT ON;
DECLARE #Dates TABLE ( dt DATE)
DECLARE #Start DATE = DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)
DECLARE #End DATE = DATEADD(YEAR, 1, #Start)
WHILE #Start <= #End
BEGIN
INSERT INTO #Dates (dt) VALUES (#Start)
SELECT #Start = DATEADD(DAY, 1, #Start)
END
; With x as
(
Select
dt
, ROW_NUMBER() OVER(PARTITION BY DATEPART(YEAR, Dt), DATEPART(MONTH, Dt) ORDER BY Dt Desc) AS rwn
From #Dates
)
Select *
From x
WHERE rwn = 1
ORDER BY Dt
This was cribbed together quick based on a couple different SO answers for the parts:
DECLARE #startdate datetime, #enddate datetime
set #startdate = '2015-12-01'
set #enddate = getdate()
;WITH T(date)
AS
(
SELECT #startdate
UNION ALL
SELECT DateAdd(day,1,T.date) FROM T WHERE T.date < #enddate
)
SELECT DISTINCT
DATEADD(
day,
-1,
CAST(CAST(YEAR(date) AS varchar) + '-' + CAST(MONTH(date)AS varchar) + '-01' AS DATETIME))
FROM T OPTION (MAXRECURSION 32767);

Select missing months between 2 dates (in same year)

I'm trying to get all months in which no transaction is placed for the same year (If different years is not possible)
This is my query to get transactions between 2 dates, but don't know how can I select only months for which transaction in database is missing
SELECT *
FROM Installment
WHERE OrderId = 1
AND InstallmentDate
BETWEEN cast('8/02/2014' as date)
AND cast('12/25/2014' as date)
InstallmentId OrderId CustomerKey InstallmentAmount InstallmentDate
18 1 INS-1 3000 2014-09-03
92 1 INS-1 3000 2014-10-13
137 1 INS-1 3000 2014-11-05
in this case record for the 12th month and 8th month is missing, how can I get this with SQL Server Query ?
Update
select yymm.yy, yymm.mm
from (select distinct year(InstallmentDate) as yy, month(InstallmentDate) as mm
from Installment
where InstallmentDate BETWEEN '2014-09-02' and '2015-01-15'
) yymm left join
Installment i
on i.OrderId = 1 and
year(i.InstallmentDate) = yymm.yy and
month(i.InstallmentDate) = yymm.mm
where i.OrderId is not null;
Gordon's query is returning all the years and months from table between 2 dates, just by changing i.OrderId is null to i.OrderId is not null here is the out of his query
yy mm
2014 9
2014 10
2014 11
Expected Output (if possible)
yy mm
2014 12
2015 1
Using the following recursive CTE:
DECLARE #start DATE = '2014-09-02'
DECLARE #end DATE = '2015-01-15'
;WITH IntervalDates (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MONTH, 1, date)
FROM IntervalDates
WHERE DATEADD(MONTH, 1, date)<=#end
)
SELECT YEAR(date) AS Year, MONTH(date) AS Month
FROM IntervalDates
you can get a list of all Years/Months between the two dates of interest:
Year Month
==============
2014 9
2014 10
2014 11
2014 12
2015 1
Using EXCEPT on the above CTE:
;WITH IntervalDates (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MONTH, 1, date)
FROM IntervalDates
WHERE DATEADD(MONTH, 1, date)<=#end
)
SELECT YEAR(date) AS Year, MONTH(date) AS Month
FROM IntervalDates
EXCEPT
SELECT DISTINCT YEAR(InstallmentDate) AS yy, MONTH(InstallmentDate) AS mm
FROM Installment
WHERE OrderId = 1 AND InstallmentDate BETWEEN #start AND #end
yields the required result set:
Year Month
=============
2014 12
2015 1
To do this in SQL, you need to start with a list of months. Assuming you have at least one record for each month in the table, you can then get the missing dates easily using a subquery. The rest of the query is just a left join and checking for non-matches:
select yymm.yy, yymm.mm
from (select distinct year(InstallmentDate) as yy, month(InstallmentDate) as mm
from Installment
where InstallmentDate BETWEEN '2014-09-02' and '2015-01-15'
) yymm left join
Installment i
on i.OrderId = 1 and
year(i.InstallmentDate) = yymm.yy and
month(i.InstallmentDate) = yymm.mm
where i.OrderId is null;
Simplest way I can think of is to have a date dimension table that contains (at least) date, and 1st of month then. For creating one take a look at something like https://dba.stackexchange.com/questions/74957/best-approach-for-populating-date-dimension-table , although that one doesn't have firstOfMonthDate in it as my example show but the idea is the same.
then your query becomes
SELECT DISTINCT
firstOfMonthDate
FROM
dateRef dr
LEFT OUTER JOIN
InstallmentDate i ON dr.date = i.InstallmentDate AND i.OrderId = 1
WHERE
i.InstallmentDate IS NULL
AND
dr.date BETWEEN #startDate and #endDate
change firstOfMonthDate for fiscal month etc. as required. This would work across any range of dates you have in your table so different years wouldn't be an issue.
Try the below script. I retrieve all dates between the specified dates and use a LEFT JOIN to get those which are not present in your table:
DECLARE #startDate AS DATETIME, #endDate AS DATETIME
DECLARE #dates AS TABLE (CurrentDate DATETIME)
SET #startDate = '2014-01-01'
SET #endDate = '2014-01-31';
with GetDates AS
(
SELECT #startDate AS TheDate
UNION ALL
SELECT DATEADD("DD", 1, TheDate) FROM GetDates
WHERE TheDate < #endDate
) INSERT INTO #dates SELECT TheDate FROM GetDates
OPTION (MAXRECURSION 0)
SELECT DISTINCT YEAR(d.CurrentDate), MONTH(d.CurrentDate) FROM #dates d
LEFT JOIN InstallmentDate i ON i.InstallmentDate BETWEEN #startDate AND #endDate AND OrderId = 1
WHERE i.InstallmentDate IS NULL
Hope this helps...

SQL Server: How to select all days in a date range even if no data exists for some days

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

Resources