Мне нужно получить последний день всех предыдущих месяцев, включая текущий месяц, до указанного месяца. Например, мне нужны последние дни сентября, августа, июля, июня, мая, апреля, марта, февраля, января, декабря 2015 года так:

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

Мне нужно указать месяц и год, к которым нужно вернуться - в приведенном выше случае это декабрь 2015 года, но это также может быть сентябрь 2015 года и тому подобное. Есть ли способ сделать цикл и сделать это вместо того, чтобы рассчитывать отдельно для каждого конца месяца?

0
Roger Dodger 6 Сен 2016 в 18:15

6 ответов

Лучший ответ

Используйте рекурсивный CTE с функцией EOMONTH.

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
4
AdaTheDev 6 Сен 2016 в 15:36
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 

Вот версия, которая идет вперед или назад по мере необходимости

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 
2
Cato 6 Сен 2016 в 16:50

Вот один из подходов, использующий CTE для создания списка увеличивающихся чисел, чтобы у нас было что выбрать и использовать в DATEADD, чтобы вернуться на соответствующее количество месяцев.

Как правило, если вы делаете это довольно часто, вместо того, чтобы генерировать числа на лету, как это с помощью CROSS JOIN, я бы рекомендовал просто создать таблицу «Числа», которая просто содержит числа от 1 до «некоторого числа, достаточно высокого, чтобы соответствовать твои нужды"

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
1
AdaTheDev 6 Сен 2016 в 15:30

Это должно масштабироваться независимо от того, как далеко вы идете назад или вперед для исходной таблицы или объекта.

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
1
djangojazz 6 Сен 2016 в 15:35

Это было быстро скомпилировано на основе пары разных SO-ответов для частей:

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);
0
Peter Tirrell 6 Сен 2016 в 15:37

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

3
Balan 6 Сен 2016 в 15:33