Как Разложить период по датам в интервале

Есть дата открытия заявки DataOpen
Есть дата закрытия заявки DataClose.

Необходимо разложить по последнему дню месяца период действия заявки в интервале. Пример

DataOpen - 2021-10-20 DataСlose - 2022-06-17

Нужно разложить в следующем порядке:

2021-10-31
2021-11-30
2021-12-31
2022-01-31
2022-02-28
2022-03-31
2022-04-30
2022-05-31
2022-06-30

В те месяца в которые заявка была открыта - дата раскладывается по последнему дню каждого месяца ,когда заявка была открыта.

Как это сделать в скрипте T SQL MS SQL STUDIO

Скажите пожалуйста.


Ответы (1 шт):

Автор решения: Andrew Nikolaev

Написал комментарии для ясности

-- Выбираем нужную на сервере базу данных
USE [YOU_DATABASE_NAME]
GO

-- Это типичные настройки, на них не нужно заострять внимания
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

-- Если функция с таким именем уже существует на сервере,
-- то мы её удаляем, чтобы создать заново (требуется для того, чтобы 
-- было легче править и вносить изменения в эту функцию, иначе пришлось 
-- бы писать этот код где то на стороне)
IF EXISTS (
     SELECT *
     FROM [dbo].[sysobjects]
     WHERE id = OBJECT_ID(N'[dbo].[fnt_GetPassengersDate]')
   )
    DROP FUNCTION [dbo].[fnt_GetPassengersDate]
GO

-- Создаём ТАБЛИЧНУЮ функцию, это говорит о том, что результатом работы
-- будет набор данных, а не скалярное значение
CREATE FUNCTION dbo.fnt_GetPassengersDate
(
    @DataOpen DATETIME,
    @DateClose DATETIME
)
RETURNS @ResultTable TABLE (Dt DATETIME)
AS
BEGIN
    -- Последний день месяца открытия
    DECLARE @LastDayOfOpenMonth DATETIME = DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(DAY, 1 - DAY(@DataOpen), @DataOpen)))
    -- Последний день месяца закрытия
    DECLARE @LastDayOfCloseMonth DATETIME = DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(DAY, 1 - DAY(@DateClose), @DateClose)))

    DECLARE @CurrentDate DATETIME = @LastDayOfOpenMonth

    WHILE @CurrentDate <= @LastDayOfCloseMonth
    BEGIN
        INSERT INTO @ResultTable VALUES (@CurrentDate)
        
        SET @CurrentDate = DATEADD(MONTH, 1, @CurrentDate)
        SET @CurrentDate = DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(DAY, 1 - DAY(@CurrentDate), @CurrentDate)))
    END 

    RETURN      
END

А потом используете её со своей таблицей через CROSS APPLY. Например вот так:

-- Создаём тестовую таблицу, у вас скорее всего будет другая
CREATE TABLE PassangersTable (UserId INT, DateOpen DATETIME, DateClose DATETIME)

-- Заполняем тестовыми данными, выдуманными из головы
INSERT PassangersTable (UserId, DateOpen, DateClose) VALUES 
(1, '2021-10-20', '2022-06-17'), 
(2, '2021-03-07', '2022-01-12'), 
(3, '2022-09-03', '2022-12-23')

SELECT 
    pt.UserId, 
    fnt.Dt 
FROM PassangersTable pt (NOLOCK)
-- в следующей строке я передаю именно ПОЛЯ исходной таблицы,
-- это будет работать так: на каждого пассажира будут разбиты 
-- периоды и все они будут объедены в один набор данных  
    CROSS APPLY dbo.fnt_GetPassengersDate(pt.DateOpen, pt.DateClose) fnt 

-- Удаляем тестовую таблицу
DROP TABLE PassangersTable
→ Ссылка