【问题标题】:Determine which of four date ranges a single date range overlaps确定单个日期范围重叠的四个日期范围中的哪一个
【发布时间】:2018-01-02 03:09:15
【问题描述】:

我的订阅可以持续任何时间(就我的输出而言),从 1 到 4 个财政季度(也可以视为四个日期范围)

我正在使用亚利桑那州财政季度:

  • 第一季度:7 月 1 日 - 9 月 30 日
  • 第二季度:10 月 1 日 - 12 月 31 日
  • 第三季度:1 月 1 日 - 3 月 31 日
  • 第四季度:4 月 1 日 - 6 月 30 日

我需要根据订阅的开始日期和结束日期计算每个季度有多少订阅处于活动状态。

例如,使用 (YYYY-MM-DD),我有:

  • 从 2016 年 7 月 6 日开始到 2017 年 2 月 22 日结束的订阅我应该能够看到我在第一季度、第二季度和第三季度有一个有效的订阅。
  • 从 2016 年 10 月 18 日开始到 2016 年 10 月 24 日结束的另一个订阅将仅被视为在第二季度有效
  • 最后,从 2016 年 9 月 28 日开始但没有结束日期的订阅将被视为在第一季度、第二季度、第三季度和第四季度有效(因此无论从第一季度到第四季度)

下面是我当前的 SQL Server 脚本,这里是 SQL Fiddle:

WITH SubscriptionInfo AS
(
    SELECT
        [Subscriptions].[Customer_Id]
        ,[DistributorTypes].[Name]                              AS [Distributor Type]
        ,[Customers].Zip_Id
        ,[Subscriptions].[UnsubscribeReason_Id]
        ,[Subscriptions].[Id]                                   AS [Subscription ID]
        ,CONVERT(DATE, [Subscriptions].[StartDate])             AS [Subscription Start Date]
        ,CONVERT(DATE, [Subscriptions].[EndDate])               AS [Subscription End Date]
        ,[PriorityLevels].PriorityLevel                         AS [Priority Level]
        ,CONVERT(DATE, [SubscriptionPriorityLevels].StartDate)  AS [Priority Level Start Date]
        ,CONVERT(DATE, [SubscriptionPriorityLevels].EndDate)    AS [Priority Level End Date]
        ,[FundingSources].[Name]                                AS [Funding Source]
        ,CONVERT(DATE, [SubscriptionFundingSources].StartDate)  AS [SubscriptionFundingSources Start Date]
        ,CONVERT(DATE, [SubscriptionFundingSources].EndDate)    AS [SubscriptionFundingSources End Date]
    FROM [Subscriptions]

    LEFT JOIN [SubscriptionPriorityLevels]
        ON [SubscriptionPriorityLevels].Subscription_Id = Subscriptions.Id
    LEFT JOIN [PriorityLevels]
        ON [PriorityLevels].Id = SubscriptionPriorityLevels.PriorityLevel_Id
    LEFT JOIN [SubscriptionFundingSources]
        ON [SubscriptionFundingSources].Subscription_Id = Subscriptions.Id
    LEFT JOIN [FundingSources]
        ON [FundingSources].Id = [SubscriptionFundingSources].FundingSource_Id
    LEFT JOIN [Customers]
        ON [Customers].Id = [Subscriptions].Customer_Id
    LEFT JOIN [DistributorTypes]
        ON [DistributorTypes].Id = Customers.DistributorType_Id

    WHERE
        ([Subscriptions].StartDate >= '2016-07-01')             -- Dummy dates, would later be parameters
        AND ([Subscriptions].EndDate <= '2017-06-30'
            OR [Subscriptions].EndDate IS NULL)
        AND ([PriorityLevels].PriorityLevel IN (2, 3))          -- Only care about these two levels
        AND ([Customers].DistributorType_Id = 1)                -- Distributor Type: Number One Distrubition
        AND ([SubscriptionFundingSources].FundingSource_Id = 2) -- Funding Source: First Bank
)

SELECT
    [SubscriptionInfo].Customer_Id
    ,[SubscriptionInfo].[Subscription ID]
    ,MAX([SubscriptionInfo].[Priority Level]) AS [Highest Priority Level]
    ,CASE   -- Determine which fiscal quarter each Subscription Start Date belongs to
        WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (7, 8, 9)       THEN 1  -- July, August, September
        WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (10, 11, 12)    THEN 2  -- October, November, December
        WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (1, 2, 3)       THEN 3  -- January, Feburary, March
                                                                                    ELSE 4  -- April, May, June
    END AS [Fiscal Quarter Start Date]
    ,CASE   -- Determine which fiscal quarter each Subscription Start Date belongs to
        WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (7, 8, 9)         THEN 1  -- July, August, September
        WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (10, 11, 12)      THEN 2  -- October, November, December
        WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (1, 2, 3)         THEN 3  -- January, Feburary, March
                                                                                    ELSE 4  -- April, May, June
    END AS [Fiscal Quarter End Date]
FROM [SubscriptionInfo]


GROUP BY
    [SubscriptionInfo].Customer_Id
    ,[SubscriptionInfo].[Subscription ID]
    ,CASE   -- Group Subscription Start Date's into Fiscal Quarters
        WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (7, 8, 9)       THEN 1  -- July, August, September
        WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (10, 11, 12)    THEN 2  -- October, November, December
        WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (1, 2, 3)       THEN 3  -- January, Feburary, March
                                                                                    ELSE 4  -- April, May, June
    END
    ,CASE   -- Group Subscription End Date's into Fiscal Quarters
        WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (7, 8, 9)         THEN 1  -- July, August, September
        WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (10, 11, 12)      THEN 2  -- October, November, December
        WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (1, 2, 3)         THEN 3  -- January, Feburary, March
                                                                                    ELSE 4  -- April, May, June
    END

ORDER BY
    [SubscriptionInfo].Customer_Id

到目前为止,我能够确定订阅开始于哪个财政季度以及结束于哪个财政季度。

我希望能够计算每个季度有多少订阅处于活动状态。

期望的输出:

| FirstQuarter | SecondQuarter | ThirdQuarter | FourthQuarter |
|--------------|---------------|--------------|---------------|
|            2 |             1 |            3 |             3 |

【问题讨论】:

  • 为什么不创建一个包含 FiscalYear、QuarterNumber 和 StartDate 的 Quarters 表?然后,您可以轻松确定给定订阅涵盖哪些季度。
  • @SeanLange 原谅我,我不确定我是否在关注。像 SQL Fiddle 这样的东西是你的建议吗?你能详细说明我将如何使用它吗?

标签: sql-server tsql date count


【解决方案1】:

按照@Sean Lange 的建议,一旦您有一个描述季度开始和结束日期的表格,您只需将订阅的开始和结束日期与季度的开始和结束日期进行比较,然后计算它们重叠的地方.基本上,如果 A.Start B.Start,两个范围会有一些重叠。

这是该模式的完整示例。

-------------------------
-- construct the quarters table (taken from the SQL Fiddle in comments above)

DECLARE @FiscalDate Table
(
  Id INT NOT NULL,
  FiscalYear INT NOT NULL,
  FiscalQuarter INT NOT NULL,
  QuarterStartDate DATE NOT NULL,
  QuarterEndDate DATE NOT NULL
  PRIMARY KEY(Id)
);

INSERT INTO @FiscalDate 
  (Id, FiscalYear, FiscalQuarter, QuarterStartDate, QuarterEndDate)
VALUES
  ( 1, 2016, 1, '2016-07-01', '2016-09-30')
  ,(2, 2016, 2, '2016-10-01', '2016-12-31')
  ,(3, 2017, 3, '2017-01-01', '2017-03-31')
  ,(4, 2017, 4, '2017-04-01', '2017-06-30')
;


-------------------------
--Get some random test data to imitate subscriptions ranges

DECLARE @tempSet Table(a date, b date)
INSERT INTO @tempSet SELECT TOP 15
    dateadd(dd, ROUND(365 * RAND(convert(varbinary, newid())), 0), '2016-07-01') as a
    , dateadd(dd, ROUND(365 * RAND(convert(varbinary, newid())), 0), '2016-07-01') as b
FROM sysobjects

-------------------------
-- Fix our random data a little (start date needs to be before end date)

DECLARE @DateRanges Table(StartDate date, EndDate date)
INSERT INTO @DateRanges
SELECT a, b FROM @tempset WHERE a <= b UNION SELECT b, a FROM @tempset WHERE b < a


-------------------------
-- Show our Date ranges in a useful order for review
SELECT * FROM @DateRanges ORDER BY StartDate, EndDate

-------------------------
-- Show our by-quarter counts.


SELECT
    fd.FiscalQuarter
    , count(*) as ActiveSubsCount
FROM
    @FiscalDate fd

    JOIN

    @DateRanges dr
        on fd.QuarterStartDate < dr.EndDate
        and fd.QuarterEndDate >= dr.StartDate
GROUP BY
    fd.FiscalQuarter


-------------------------
-- and in your desired output (without your quarters table)
SELECT
    COUNT(CASE WHEN '2016-07-01' < dr.EndDate AND '2016-09-30' >= dr.StartDate THEN 1 ELSE NULL END) AS FirstQuarter 
    , COUNT(CASE WHEN '2016-10-01' < dr.EndDate AND '2016-12-31' >= dr.StartDate THEN 1 ELSE NULL END) AS SecondQuarter  
    , COUNT(CASE WHEN '2017-01-01' < dr.EndDate AND '2017-03-31' >= dr.StartDate THEN 1 ELSE NULL END) AS ThirdQuarter 
    , COUNT(CASE WHEN '2017-04-01' < dr.EndDate AND '2017-06-30' >= dr.StartDate THEN 1 ELSE NULL END) AS FourthQuarter 
FROM
    @DateRanges dr

【讨论】:

  • 哇,这很有帮助!我已经在另一个SQL Fiddle Script 中将您的代码转换为MS SQL Server,我很确定您已经完成了我需要的工作,并且看起来很简单!非常感谢!
  • 是的,这就是我要说的。干得好。
猜你喜欢
  • 1970-01-01
  • 2019-08-29
  • 1970-01-01
  • 2011-07-02
  • 2013-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多