Разбивка диапазона дат на количество месяцев в каждом календарном году - PullRequest
1 голос
/ 12 марта 2019

У меня есть большое количество диапазонов дат, как показано ниже, пример ниже. Мне нужно рассчитать, сколько месяцев в каждом фактическом календарном году. Так что это будет выглядеть так:


Контракт: 123

Дата начала: 01/11 / 2016

Дата окончания: 01/06/2018

  • 01.11.2016> 31.12.2016 - 2 месяца 2016 года
  • 01/01/2017> 31.12.2017 - 12 месяцев в году 2017
  • 01/01/2018> 01/06/2018 - 6 месяцев в 2018 году

Контракт: 456

Дата начала: 31/05/2017

Дата окончания: 01/06/2019

  • 31.05.2017> 31.12.2017 - 6 месяцев в году 2017
  • 01/01/2018> 31/12/2018 - 12 месяцев в году 2018
  • 01/01/2019> 01/06/2019 - 6 месяцев в году 2019

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

Я изначально шел по маршруту CTE, но это взорвало мой разум.

Ожидаемый результат:

contract_id    year    number of months
123            2016    2
123            2017    12
123            2018    6
456            2017    6
456            2018    12
456            2019    6

Или аналогично, я более чем рад изменить исходный запрос, чтобы включить в него наилучший результат / метод для достижения этого.


Определение таблицы:

  • contract_id: int
  • start_date: datetime
  • end_date: datetime

    contract_id start_date end_date 123 2016-01-11 00: 00: 00.000 2018-06-01 00: 00: 00.000 456 2017-05-31 00: 00: 00.000 2019-06-01 00: 00: 00.000

Ответы [ 3 ]

1 голос
/ 12 марта 2019

Я бы использовал подсчет для этого.Я держу один в моей системе как молниеносный вид.Вот вид.

create View [dbo].[cteTally] as

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 N from cteTally
GO

Тогда нам понадобятся некоторые примерные данные.Примерно так.

declare @Something table
(
    Contract char(1)
    , StartDate date
    , EndDate date
)

insert @Something values
('A', '20161101', '20180601')
, ('B', '20170531', '20190601')

Теперь мы можем запросить образец и использовать таблицу подсчета для краткой обработки этого.

select s.Contract
    , ContractYear = datepart(year, DATEADD(month, t.N - 1, s.StartDate))
    , NumMonths = count(*)
from @Something s
join cteTally t on t.N <= datediff(month, s.StartDate, s.EndDate) + 1
group by s.Contract
    , datepart(year, DATEADD(month, t.N - 1, s.StartDate))
order by s.Contract
    , datepart(year, DATEADD(month, t.N - 1, s.StartDate))
0 голосов
/ 12 марта 2019

Вот такая возможность:

select t.contract_id,n.id as year,q2.[#months]
from yourtable t
cross apply
(
    select year([Start Date]) as first_year,
    select year([End Date]) as last_year
)q
inner join numbers_table n on n.id between q.first_year and q.last_year
cross apply
(
    select case
        when n.id=first_year then 12-month([Start Date])
        when n.id=last_year then month([End Date])
        else 12
        end as [#months]
)q2

Если у вас нет таблицы чисел, поставьте ее перед запросом:

;WITH numbers_table(id) AS
(
  SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
  FROM sys.all_columns AS s1
  CROSS JOIN sys.all_columns AS s2
)

Я бы пошел на проверенный вариант ШонаЛанге, хотя

0 голосов
/ 12 марта 2019

Вы можете использовать master..spt_values ​​с type = 'P' для получения чисел от 0 до 2047. Фильтрация этого числа происходит между годом даты начала и годом даты окончания, и вы получаете годы между двумя датами какстрок.EndOfYear и BeginOfYear возвращают первую дату или последнюю дату каждого из этих лет.Months возвращает месяцы между первой и последней датой.

DECLARE @Table TABLE
(
    Contract VARCHAR(5),
    StartDate DATETIME,
    EndDate DATETIME
)

INSERT INTO @Table(Contract, StartDate, EndDate) 
SELECT 'A', '20161101', '20180601' UNION ALL
SELECT 'B', '20170531', '20190601'

SELECT Contract,
    Year = spt_values.number,
    Months = Months.Value
FROM @Table CROSS JOIN
    master..spt_values CROSS APPLY
    (
        SELECT CAST(CONCAT(spt_values.number, '1231') AS DATETIME) AS Value
    ) AS EndOfYear CROSS APPLY
    (
        SELECT DATEADD(YEAR, -1, EndOfYear.Value) + 1 AS Value
    ) AS BeginOfYear CROSS APPLY
    (
        SELECT DATEDIFF(MONTH, IIF(BeginOfYear.Value < StartDate, StartDate, BeginOfYear.Value), IIF(EndOfYear.Value > EndDate, EndDate, EndOfYear.Value)) + 1 AS Value
    ) Months
WHERE type = 'P' AND
    spt_values.number <= YEAR(EndDate) AND
    spt_values.number >= YEAR(StartDate)
...