当前位置: 首页 > 知识库问答 >
问题:

将日期范围细分为每个日历年的月数

闾丘诚
2023-03-14

我有大量的日期范围,如下所示,示例如下。我需要计算每个实际日历年有多少个月。因此,这将分解为:

合同:123

开始日期:2016年11月1日

结束日期:2018年6月1日

  • 2016年11月1日

合同:456

开始日期:2017年5月31日

结束日期:2019年6月1日

  • 2017年5月31日

有人知道解决这个问题的方法吗?每个合同都有一行,都在同一个表中,开始和结束日期列在同一行中。

我原本打算沿着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:日期时间

    合同编号开始日期结束日期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个答案

    陈欣荣
    2023-03-14

    您可以使用具有type='P'的master...spt_values来获取从0到2047的数字。过滤此数字以使其介于开始日期的年份和结束日期的年份之间,您可以将两个日期之间的年份作为行获取。EndOfYork和Start inOfYork分别返回今年的第一个日期和最后一个日期。月份返回第一个日期和最后一个日期之间的月份。

    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)
    
    柯书
    2023-03-14

    有一种可能性:

    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
    )
    

    不过我还是会选肖恩·兰格的测试版

    云捷
    2023-03-14

    我会用一个计数来计算这个。我在我的系统上保留了一个视图,它的速度非常快。这是视图。

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

    现在,我们可以查询样本并利用tally表进行简短的工作。

    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))
    
     类似资料:
    • 问题内容: 我想将日期范围划分为各个月份。 例如-我有一个以以下方式包含数据的视图: 我希望能够以这种方式显示以上数据: 有人可以帮我吗? 问题答案: 下面的查询应该可以解决问题。该(该条款)动态生成一些月份的数据,我们可以使用它来加入反对。

    • 问题内容: 我有一个像这样的简单模型: 我想输出按月细分: 一个月内有多少笔交易() 合并值() 我不确定最好的攻击方法是什么。我已经看到了一些看上去很吓人的额外选择查询,但我的简单想法是告诉我,最好是迭代数字,从任意开始的年/月开始,一直计数到我达到当月,然后简单地抛出查询该月的过滤条件。更多数据库工作-减轻开发人员压力! 什么对你最有意义?有什么好方法可以拉回快速数据表吗?还是我的肮脏方法可能

    • 似乎不能处理一个数字的月份中的某一天: 在此示例中,正确解析日期,但引发异常。如果我使用零填充日期,例如“05/03/1969”,则两者都起作用。但是,如果每月的日期或每年的月份都是个位数,则将引发异常。 什么是格式来解析一个数字和两位数的月份和一年中的月份?

    • 问题内容: 我正在使用MySQL,并且具有下表: 我希望能够生成这样的报告,其中在过去4周内完成了各个周期: 或最近3个月内: 有什么想法可以进行选择查询以生成等效的日期范围和点击次数吗? 问题答案:

    • 感谢所有人!

    • 我试图在我正在创建的C#应用程序中显示两个日期之间的年、月和日。(使用控制台进行测试) 我正在使用NodaTime来实现这一点,但在接下来的几个月里,我遇到了一些问题。 我已经阅读了这里的大部分问题和答案,但没有找到任何我可以从中受益的东西。 大多数时候函数工作但有时它不添加月如果天 我使用这个代码进行测试 (2017, 10, 16)和(2018, 1, 15)应该显示为3个月,但它们显示为2个