Как создать таблицу календаря только на конец каждого месяца SQL Server 2017 - PullRequest
0 голосов
/ 06 января 2020

Какой был бы самый простой способ создать календарную таблицу или CTE, которые возвращали бы дату конца месяца каждого года?

Примерно так:

enter image description here

Ответы [ 4 ]

4 голосов
/ 06 января 2020

Возможно, не самый простой, но самый эффективный способ с подсчетом:

WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
    FROM N N1, N N2) --Add more for more months
SELECT DATEADD(MONTH, T.I, '20000101') AS MonthStart,
       EOMOMTH(DATEADD(MONTH, T.I, '20000101')) AS MonthEnd 
FROM Tally T;
2 голосов
/ 06 января 2020

Для этого можно использовать рекурсивное общее табличное выражение:

with cte as (
    select cast('2018-01-31' as date) EndOfMonth
    union all
    select eomonth(dateadd(month, 1, EndOfMonth)) 
    from cte 
    where EndOfMonth < cast('2018-12-01' as date)
)
select * from cte

Элемент привязки запускается в последний день января 2018 г., а рекурсивный элемент добавляет 1 месяц на итерацию, используя eomonth() для всегда возвращайте последний день месяца, пока не наступит декабрь 2018 года. При необходимости вы можете легко настроить дату начала и окончания.

Демонстрация на DB Fiddle

| EndOfMonth          |
| :------------------ |
| 31/01/2018 00:00:00 |
| 28/02/2018 00:00:00 |
| 31/03/2018 00:00:00 |
| 30/04/2018 00:00:00 |
| 31/05/2018 00:00:00 |
| 30/06/2018 00:00:00 |
| 31/07/2018 00:00:00 |
| 31/08/2018 00:00:00 |
| 30/09/2018 00:00:00 |
| 31/10/2018 00:00:00 |
| 30/11/2018 00:00:00 |
| 31/12/2018 00:00:00 |

Редактировать

Как прокомментировал @squillman: если вы планируете создавать календарь на более чем 7 лет, то вам нужно добавить условие option(maxrecursion 0) в конце запроса.

Как прокомментировал @ Ларну: производительность рекурсивного запроса падает, когда набор данных становится больше. Если вам нужно создать очень большой календарь (например, несколько столетий), тогда другие варианты лучше.

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

2 голосов
/ 06 января 2020

В этом случае будет работать функция EOMONTH, см. Последнее поле в запросе ниже.

DECLARE @StartDate DATETIME = '01/01/2015' 
DECLARE @EndDate DATETIME = '12/01/2016' 

;WITH OrderedDays as 
( 
    SELECT CalendarDate = @StartDate
    UNION ALL 
    SELECT CalendarDate = DATEADD(MONTH, 1, CalendarDate)
    FROM OrderedDays WHERE DATEADD (MONTH, 1, CalendarDate) <= @EndDate 
),
Calendar AS
(
    SELECT
        EndOfMonth =  EOMONTH ( CalendarDate ) 
    FROM
        OrderedDays                 
) 
SELECT * FROM Calendar
OPTION (MAXRECURSION 0) 
1 голос
/ 07 января 2020

U может использовать системную таблицу master..spt_values. Вот пример кода:

SELECT    DATEADD([dd], -1, DATEFROMPARTS(2020, [number] + 1, 1))
    FROM  master..spt_values
    WHERE [number] BETWEEN 1 AND 11
          AND [type] = 'P'
UNION
SELECT dateadd(dd, -1, DATEFROMPARTS(2021, 1, 1))

РЕДАКТИРОВАТЬ: ОК, спасибо @Ross Bu sh, есть код лучше и меньше:

SELECT    EOMONTH(DATEFROMPARTS(2020, [number], 1))
    FROM  master..spt_values
    WHERE [number] BETWEEN 1 AND 12
          AND [type] = 'P'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...