SQL: union multiple single row results sets from the same query - sql-server

I have a query that, when given a specific date, examines a large dataset (around 36m and growing) and returns an aggregate. Everything is working as expected... However, my end goal is to be able to determine a yearly average of these values. Perhaps my brain is running on empty, but I'm trying to do this dynamically where I don't have to run the query 365 times and then average.....
I need to find the yearly average of the 365 results, per #inst, per #program.
Any pointers in the right direction would be most appreciated.
Query:
USE HCODS
GO
DECLARE #user_date DATETIME
DECLARE #inst VARCHAR(4)
DECLARE #program VARCHAR(4)
SELECT #user_date = '9/30/2016'
SELECT #inst = 'SAC'
SELECT #program = 'PSU';
WITH T AS (
SELECT
B.OFFENDERID
,Institution = I.ORGCOMMONID
,BedUse = B.BEDUSE
,BeginEffectiveDtTm = CAST(B.BEDASSIGNMENTDATE AS DATETIME) + CAST(B.BEDASSIGNMENTTIME AS DATETIME)
,EndEffectiveDtTm = CASE WHEN B.BEDASSIGNMENTSTATUS = 'U' THEN
(CAST(B.INMBEDSTATUSDATE AS DATETIME) + CAST(B.INMBEDSTATUSTIME AS DATETIME)) ELSE NULL END
FROM ODS.BEDASSIGNMENT (NOLOCK) B
INNER JOIN (
SELECT F.PARTYID, I.ORGCOMMONID
FROM ODS.ORGANIZATIONPROF (NOLOCK) AS F
INNER JOIN ODS.ORGANIZATIONPROF (NOLOCK) AS I ON F.ORGAREACODE = I.PARTYID
) AS I ON B.FACILITYWHEREBEDLOCATED = I.PARTYID
WHERE B.BEDASSIGNMENTDATE BETWEEN '1/1/2016' AND '12/31/2016'
AND B.BEDASSIGNMENTSTATUS IN ('U','M')
)
SELECT CAST(#user_date AS DATE)
,T.INSTITUTION
,T.BEDUSE
,COUNT(*)
FROM T
WHERE
(
(
T.BEGINEFFECTIVEDTTM <= DATEADD(second,-1,(#user_date+1))
AND
T.ENDEFFECTIVEDTTM >= #user_date
)
OR T.ENDEFFECTIVEDTTM IS NULL
)
AND T.INSTITUTION = #inst
AND T.BedUse = #program
GROUP BY
T.Institution
,T.BedUse
Result sets (each one obtained by a single running of the query)
Date |Institution |BedUse |Count
-----------|------------|-------|-------
2016-09-30 |SAC |PSU |446
2016-10-01 |SAC |PSU |421
2016-10-02 |SAC |PSU |423
etc......

While it is hard to answer the question of your data without seeing it. I can turn you onto a SQL concept of windowed functions. This in essense is doing an inline grouping to aggregate data. If I have a single set but want to fashion multiple statements over it to see different things, this statement is perfect.
So in an example I am in essence going from the 1st of January of 2015 to today(dynamic as this could be any day even after I post this). I am then picking a random number of 1 to 100 to populate my row of data in my temporary set with. I then can do my aggregate operations on that.
DECLARE #Data TABLE ( Id INT IDENTITY, val INT, dt DATETIME)
DECLARE #Start DATETIME = '1-1-2015'
SET NOCOUNT ON;
WHILE #Start <= GETDATE()
BEGIN
INSERT INTO #Data VALUES (ABS(CHECKSUM(NewId())) % 100, #Start)
SELECT #Start = DATEADD(DAY, 1, #STart)
END
SELECT DISTINCT
SUM(Val) OVER() AS TotalValues
, COUNT(*) OVER() AS rowCounts
, DATEADD(YEAR, DATEDIFF(YEAR, 0, Dt), 0) AS YearDate
, COUNT(*) OVER(PARTITION BY DATEADD(YEAR, DATEDIFF(YEAR, 0, Dt), 0)) AS DaysInYear
, SUM(Val) OVER(PARTITION BY DATEADD(YEAR, DATEDIFF(YEAR, 0, Dt), 0)) AS ValsByYear
, AVG(Val) OVER(PARTITION BY DATEADD(YEAR, DATEDIFF(YEAR, 0, Dt), 0)) AS AVGByYear
, DATEADD(Month, DATEDIFF(Month, 0, Dt), 0) AS MonthDate
, COUNT(*) OVER(PARTITION BY DATEADD(Month, DATEDIFF(Month, 0, Dt), 0)) AS DaysInMonth
, SUM(Val) OVER(PARTITION BY DATEADD(Month, DATEDIFF(Month, 0, Dt), 0)) AS ValsByMonth
, AVG(Val) OVER(PARTITION BY DATEADD(Month, DATEDIFF(MOnth, 0, Dt), 0)) AS AVGByMonth
From #Data

Related

Generate counts of records for each day in a week

I use SQL Server 2014 for my project. I have the following code to produce the number of registrations of each day:
SELECT
DATEADD(DAY, DATEDIFF(DAY, 0, createTime), 0) AS createdOn,
COUNT(*) AS Count
FROM
Registration
GROUP BY
DATEADD(DAY, DATEDIFF(DAY, 0, createTime), 0)
ORDER BY
createdOn
Now I would like to get the numbers for each day in a week (so there will be max 7 rows in output). How can I do it?
Here is the solution I have based on George's comment. Thank you, George!
SELECT
DATEPART(weekday, createTime) AS createdOn,
COUNT(*) AS Count
FROM
Registration
GROUP BY
DATEPART(weekday, createTime)
ORDER BY
createdOn
One way to return all days within a range returned with your data joined on matching days is to use a "calendar" table and LEFT JOIN your data by date.
DECLARE #StartDate DATETIME = '01/01/2015'
DECLARE #EndDate DATETIME = '12/01/2016'
//By Day In Year
;WITH Calender as
(
SELECT CalendarDate = #StartDate
UNION ALL
SELECT CalendarDate = DATEADD(DAY, 1, CalendarDate)
FROM Calender WHERE DATEADD (DAY, 1, CalendarDate) <= #EndDate
)
SELECT
C.CalendarDate,
COUNT(*) AS Count
FROM
Calender C
LEFT JOIN Regsitration R ON R.createdOn = C.CalendarDate
GROUP BY
C.CalendarDate
OPTION (MAXRECURSION 0)
//By Week In Year
;WITH Calender as
(
SELECT CalendarDate = #StartDate, WeekNumber=DATEPART(WEEK, #StartDate)
UNION ALL
SELECT CalendarDate = DATEADD(WEEK, 1, CalendarDate), WeekNumber=DATEPART(WEEK, #StartDate)
FROM Calender WHERE DATEADD (WEEK, 1, CalendarDate) <= #EndDate
)
SELECT
C.WeekNumber,
COUNT(*) AS Count
FROM
Calender C
LEFT JOIN Regsitration R ON DATEPART(WEEK,R.createdOn) = C.WeekNumber
GROUP BY
C.WeekNumber
OPTION (MAXRECURSION 0)

How to display monthly balance for month with no balance SQL Server

I have the following block of code which I am using to query a SQL Server database that summarizes all balances for each of the previous 13 months. It works pretty good, but there are a few months when there were no balances to report. These months are not displaying which I do need. At this point, I am at a loss for what to try next.
DECLARE #StartDate DATE, #EndDate DATE;
SELECT
#StartDate = CONVERT(VARCHAR(11), DATEADD(month, -13, GETDATE())),
#EndDate = CONVERT(VARCHAR(11), DATEADD(month, 0, GETDATE()));
;WITH d(d) AS
(
SELECT
DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, #StartDate), 0))
FROM
(SELECT TOP (DATEDIFF(MONTH, #StartDate, #EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM
sys.all_objects ORDER BY [object_id]) AS n
)
SELECT
FORMAT(d.d, 'MMM yy') AS Purchase_Date,
CAST(ROUND(SUM(lm.BALANCE), 0) AS FLOAT) AS Balance
FROM
d
LEFT OUTER JOIN
dbo.purchases AS lm ON lm.purchase_date >= d.d
AND lm.purchase_date < DATEADD(MONTH, 1, d.d)
WHERE
lm.Buyer_code = 'FirstTime'
AND lm.PROGRAM_ID = 'NewBuyers'
GROUP BY
d.d
ORDER BY
d.d
You are overriding the LEFT JOIN by the where clause, which requires that every row have certain values from the left joined table. In effect it is equivalent to an inner join. You need to allow rows from d to survive into the result, which you can do by using the wanted conditions directly in the LEFT JOIN:
SELECT
FORMAT( d.d, 'MMM yy' ) AS Purchase_Date
, CAST( ROUND( SUM( lm.BALANCE ), 0 ) AS float ) AS Balance
FROM d
LEFT OUTER JOIN DBO.purchases AS lm ON lm.purchase_date >= d.d
AND lm.purchase_date < DATEADD( MONTH, 1, d.d )
AND lm.Buyer_code = 'FirstTime'
AND lm.PROGRAM_ID = 'NewBuyers
GROUP BY
d.d
ORDER BY
d.d

When an UPDATE statement has access to multiple values, which one does it choose?

I didn't think this code would work, and now that is seems to be working I'm worried that it might not always work:
IF OBJECT_ID('tempdb..#Dates') IS NOT NULL DROP TABLE #Dates
; WITH Dates
AS (
SELECT CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS DATE) AS CDate
UNION ALL
SELECT DATEADD(dd, 1, CDate) AS CDate
FROM Dates
WHERE DATEADD(dd, 1, CDate) < CAST(GETDATE() AS DATE)
)
SELECT d.CDate
, d.CDate AS BDate
INTO #Dates
FROM Dates d
OPTION (MAXRECURSION 400)
; WITH BDate
AS (
SELECT CDate
FROM #Dates d
WHERE CDate NOT IN ('2018-01-01', '2018-01-15', '2018-02-19') -- New Years, MLK Day, Presidents Day
AND DATEPART(dw, d.CDate) NOT IN (1,7)
)
UPDATE d
SET d.BDate = b.CDate
FROM #Dates d
JOIN BDate b
ON d.CDate <= b.CDate
SELECT * FROM #Dates
What I don't understand is how it knows which value to choose for the UPDATE statement. If we choose a random date like January 24th, a SELECT statement would give us multiple values (since that's a Wednesday and we're avoiding weekends, that list would include January 24th, 25th and 26th, and then jump to 29th, 30th, 31st, etc.). So why is it choosing the one I actually want (the minimum value that meets the criteria)? It won't let me use ORDER BY to force the order, of course.
Normally I would write my code more like this:
IF OBJECT_ID('tempdb..#Dates') IS NOT NULL DROP TABLE #Dates
; WITH Dates
AS (
SELECT CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS DATE) AS CDate
UNION ALL
SELECT DATEADD(dd, 1, CDate) AS CDate
FROM Dates
WHERE DATEADD(dd, 1, CDate) < CAST(GETDATE() AS DATE)
)
SELECT d.CDate
, d.CDate AS BDate
INTO #Dates
FROM Dates d
OPTION (MAXRECURSION 400)
; WITH BDate
AS (
SELECT CDate
FROM #Dates d
WHERE CDate NOT IN ('2018-01-01', '2018-01-15', '2018-02-19') -- New Years, MLK Day, Presidents Day
AND DATEPART(dw, d.CDate) NOT IN (1,7)
)
, BDMin
AS (
SELECT d.CDate
, MIN(b.CDate) AS BDate
FROM #Dates d
JOIN BDate b
ON d.CDate <= b.CDate
GROUP BY d.CDate
)
UPDATE d
SET d.BDate = b.BDate
FROM #Dates d
JOIN BDMin b
ON d.CDate = b.CDate
SELECT * FROM #Dates
It certainly seems safer, but now I'm wondering if it's necessary.

Performance tuning for SQL Query

Hi here I am attaching my sample table structure which I want to use in my project
CREATE TABLE TESTSALESVOLUMETABLE
(
ID INT IDENTITY(1,1),
AMOUNT DECIMAL(18,2),
CREDITEDDATE DATETIME
)
and the queries I used like this
DECLARE #CURRENTDATE AS DATETIME = GETDATE()
DECLARE #PSV AS INT = 0
DECLARE #TOTAL AS INT = 0
IF (DATEPART(DAY, #CURRENTDATE) <= 15)
BEGIN
SELECT #PSV = (
SELECT Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE DATEPART(DAY, CREDITEDDATE) <= 15
AND MONTH(CREDITEDDATE) = MONTH(#CURRENTDATE)
AND YEAR(CREDITEDDATE) = YEAR(#CURRENTDATE)
)
END
ELSE
BEGIN
SELECT #PSV = (
SELECT Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE DATEPART(DAY, CREDITEDDATE) > 15
AND MONTH(CREDITEDDATE) = MONTH(#CURRENTDATE)
AND YEAR(CREDITEDDATE) = YEAR(#CURRENTDATE)
)
END
SELECT #total = (
SELECT Sum(Amount)
FROM TESTSALESVOLUMETABLE
)
SELECT #PSV 'PSV',
#total 'TOTAL'
Is there any way to increase the performance of this query
First, you don't need a subquery for setting the variable. Second, the use of functions on columns usually prevents the use of indexes. So, I would recommend something like this:
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 1 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE));
Then, you want an index on TESTSALESVOLUMETABLE(CREDTEDDATE, AMOUNT).
Following the guidelines from: Bad habits to kick : mis-handling date / range queries - Aaron Bertrand - 2009-10-16
First, we want to get rid of:
where datepart(day, crediteddate) <= 15
and month(crediteddate)=month(#currentdate)
and year(crediteddate)=year(#currentdate)
because:
[...] you've effectively eliminated the possibility of SQL Server taking advantage of an index. Since you've forced it to build a nonsargable condition, this means it will have to convert every single value in the table to compare it to the [value] you've presented on the right hand side [...]
Second, we want to make sure to avoid using between with datetimes because it can return unwanted rows or miss wanted rows, even when using something like between ... and dateadd(second, -1, #thrudate) or even between ... and 'yyyy-mm-ddT23:59:59.997'. (See Aaron Bertrand's article for more examples on this).
So the best way to do this would be to say:
If today is the 15th or earlier, get rows >= the 1st of this month and < the 16th of this month
If today is the 16th or later, get rows >= the 16th of this month and < the 1st of next month
Also, as Gordon Linoff mentioned, you will benefit from an index on testsalesvolumetable(crediteddate, amount). But Gordon's formulas always return the 1st and 16th of the current month.
Instead of breaking the procedure into two queries depending on the current day, we can calculate those from and thru dates and just use one query.
Here is example code both with and without using variables for the from and thru dates, along with a quick calendar test to check the resulting ranges.
rextester link for test setup: http://rextester.com/YVLI65217
create table testsalesvolumetable (crediteddate datetime not null, amount int not null)
insert into testsalesvolumetable values
('20161201',1) ,('20161202',1) ,('20161203',1) ,('20161204',1) ,('20161205',1)
,('20161206',1) ,('20161207',1) ,('20161208',1) ,('20161209',1) ,('20161210',1)
,('20161211',1) ,('20161212',1) ,('20161213',1) ,('20161214',1) ,('20161215',1)
,('20161216',1) ,('20161217',1) ,('20161218',1) ,('20161219',1) ,('20161220',1)
,('20161221',1) ,('20161222',1) ,('20161223',1) ,('20161224',1) ,('20161225',1)
,('20161226',1) ,('20161227',1) ,('20161228',1) ,('20161229',1) ,('20161230',1)
,('20161231',1) ,('20170101',1)
/* ----- without variables */
declare #psv int;
select #psv = Sum(amount)
from testsalesvolumetable
where crediteddate >= dateadd(day, (1- (day(convert(date,getdate()))/16)) - (day(convert(date,getdate()))%16), convert(date,getdate()))
and crediteddate < case
when day(convert(date,getdate()))>15
then dateadd(month, datediff(month, -1, convert(date,getdate())), 0)
else dateadd(day,15,dateadd(month, datediff(month, 0, convert(date,getdate())), 0))
end;
select psv=#psv;
--*/
/* ----- with variables */
--declare #psv int;
declare #currentdate date;
/* change to date datatype to get rid of time portion*/
set #currentdate = getdate();
--set #currentdate = '20161212'
declare #fromdatetime datetime;
declare #thrudatetime datetime;
set #fromdatetime = dateadd(day, (1- (day(#currentdate)/16)) - (day(#currentdate)%16), #currentdate);
set #thrudatetime = case
when day(#currentdate)>15
then dateadd(month, datediff(month, -1, #currentdate), 0)
else dateadd(day,15,dateadd(month, datediff(month, 0, #currentdate), 0))
end;
select #psv = sum(amount)
from testsalesvolumetable
where crediteddate >= #fromdatetime
and crediteddate < #thrudatetime;
--/*
select
psv=#psv
, CurrentDate =convert(varchar(10),#currentdate ,121)
, FromDateTime=convert(varchar(10),#fromdatetime,121)
, ThruDateTime=convert(varchar(10),#thrudatetime,121);
--*/
Rextester link for the calendar test: http://rextester.com/ESZRH30262
--/* ----- Calendar Test */
;with n as (
select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(n)
)
, cal as (
select DateValue=convert(datetime,dateadd(day, row_number() over (order by (select 1)) -1, '20160101'))
from n as a
cross join n as b
cross join n as c
cross join n as d
)
select
--DateValue=convert(varchar(10),DateValue,121)
minDate =convert(varchar(10),min(DateValue),121)
, maxDate =convert(varchar(10),max(DateValue),121)
, FromDatetime=convert(varchar(10),dateadd(day, (1- (day(DateValue)/16)) - (day(DateValue)%16), DateValue),121)
, ThruDatetime=convert(varchar(10),case
when day(DateValue)>15
then dateadd(m, datediff(m, -1, DateValue), 0)
else convert(varchar(10),dateadd(day, 16 - day(DateValue), DateValue),121)
end,121)
, GordonFrom = convert(varchar(10),dateadd(day, 1 - day(DateValue), cast(DateValue as date)),121)
, GordonThru = convert(varchar(10),dateadd(day, 16 - day(DateValue), cast(DateValue as date)),121)
from cal
where datevalue >= '20160101'
and datevalue < '20170101'
--/*
group by
convert(varchar(10),dateadd(day, (1- (day(DateValue)/16)) - (day(DateValue)%16), DateValue),121)
, convert(varchar(10),case
when day(DateValue)>15
then dateadd(m, datediff(m, -1, DateValue), 0)
else convert(varchar(10),dateadd(day, 16 - day(DateValue), DateValue),121)
end,121)
, convert(varchar(10),dateadd(day, 1 - day(DateValue), cast(DateValue as date)),121)
, convert(varchar(10),dateadd(day, 16 - day(DateValue), cast(DateValue as date)),121)
order by FromDateTime
I thing this will work fine
DECLARE #PSV AS INT = 0
DECLARE #TOTAL AS INT = 0
IF (DATEPART(DAY,GETDATE()) <= 15)
BEGIN
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 1 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE));
END
ELSE
BEGIN
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 31 - DAY(GETDATE()), CAST(GETDATE() as DATE));
END
SELECT #total = (
SELECT Sum(Amount)
FROM TESTSALESVOLUMETABLE
)
SELECT #PSV 'PSV',
#total 'TOTAL'

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);

Resources