【问题标题】:TSQL: Continuous period for whole year / each monthSQL:全年/每月连续周期
【发布时间】:2019-11-19 19:50:00
【问题描述】:

我试图找到所有Cust 在 2018 年每个月至少有一天的会员资格。

我想出了在每个月的月初/中/月底检查他们的会员资格的解决方案,就像下面的 sn-p 一样,但试图找到更智能的解决方案。

我知道我可以在 365 天中的每一天使用计数表来检查这一点,但可能有更优雅的解决方案?我对 SQL 有点陌生,我想我在GROUPing 区域缺少一些东西。

在下面显示的代码 sn-p 中,Cust 都至少有一天的会员资格。

期望的输出:

CustID
------
   1
  22

代码:

with data as 
(
    select * 
    from (values (1, 1,   '2017-12-11', '2018-01-16'),   (1, 22,  '2018-01-28', '2018-03-9' ), (1, 333, '2018-03-1', '2018-12-31') ,  -- island
                 (22, 1,  '2017-12-31', '2018-01-11'),   (22, 2,  '2017-2-11',  '2019-12-31')) as t (CustID, ContractID, StartDD, EndDD)     ---      
    )
    select 
        isdate(startDD), isdate(EndDD) 
    from 
        data
), gaps as 
(
    select  
        *,  
        datediff(day, lag(EndDD, 1, StartDD) over (partition by CustID order by StartDD), StartDD) as BreakDD          -- negative is island
    from 
        data
)
select 
    *, 
    datepart(month,StartDD) mmS , datepart(month,EndDD) mmE 
from 
    gaps 
    -- and was active any 1+ day during each of the 12 months in 2018    ????
where 
    1 = 1 
    /* and (cast('1/1/2018' as date) between StartDD and EndDD
            or cast('1/15/2018' as date) between StartDD and EndDD     
            or cast('1/31/2018' as date) between StartDD and EndDD)
       ---- etc..  for each month
       and (      cast('12/1/2018'  as date)   between  StartDD  and  EndDD 
              or  cast('12/15/2018' as date)   between  StartDD  and  EndDD  
              or  cast('12/31/2018' as date)   between  StartDD  and  EndDD  
           ) 
*/ 
--select CustID, max(BreakDD) Max_Days
--from gaps
--group by CustID

【问题讨论】:

  • 提供你的 DDL 和一些示例数据。
  • 这是带有计数的版本:select distinct custID, datepart(month,dd) -- gaps.*, t.dd from gaps join ( select top 365 cast ( dateadd(day, row_number() over (order by (select 1)) -1 , '1-1-2018') as date) as dd from master..spt_values ) t on t.dd 在 StartDD 和 EndDD 之间
  • 试试我的答案。希望对您有所帮助。

标签: sql-server tsql


【解决方案1】:

试试这个答案。

首先创建一个函数来返回给定日期之间的所有月份和年份。

功能:

--SELECT * FROM dbo.Fn_GetMonthYear('2017-12-11','2018-01-16')
ALTER FUNCTION dbo.Fn_GetMonthYear(@StartDate  DATETIME,@EndDate    DATETIME)
RETURNS TABLE
AS

    RETURN(
    SELECT  DATEPART(MONTH, DATEADD(MONTH, x.number, @StartDate)) AS [Month]
            ,DATEPART(YEAR, DATEADD(MONTH, x.number, @StartDate)) AS [Year]
    FROM    master.dbo.spt_values x
    WHERE   x.type = 'P'        
    AND     x.number <= DATEDIFF(MONTH, @StartDate, @EndDate)
    )

表架构:

CREATE TABLE #t(CustID INT, ContractID INT, StartDD date, EndDD date)

INSERT INTO #t values (1, 1,   '2017-12-11', '2018-01-16'),   (1, 22,  '2018-01-28', '2018-03-9' ), (1, 333, '2018-03-1', '2018-12-31') ,  -- island
                 (22, 1,  '2017-12-31', '2018-01-11'),   (22, 2,  '2017-2-11',  '2019-12-31')

这是满足您要求的 T-SQL 查询

SELECT CustID
    ,COUNT(DISTINCT [Month]) NoOfMonths
FROM(
    SELECT * 
    FROM #t t
    CROSS APPLY dbo.Fn_GetMonthYear(StartDD,EndDD)
    )D
WHERE [Year] = 2018
GROUP BY CustID
HAVING COUNT(DISTINCT [Month])=12

结果:

CustID  NoOfMonths
1       12
22      12

【讨论】:

  • 谢谢 D!!!您的 Fn_GetMonthYear 是特定于用户的,并且看起来像理货表,对吗?你能建议你的 fn_ 的输出是什么,或者将它调整为我之前粘贴的通用计数。再次发送
  • @Mich28,是的,它像计数表或数字表。
  • @Mich28,这个答案有用吗?
【解决方案2】:

找到所有会员资格至少一天的客户 2018年的月份

我认为这意味着每个custid 的数据必须存在于'2018-01-01''2018-12-31' 之间。

CREATE TABLE #t(CustID INT, ContractID INT, StartDD date, EndDD date)

INSERT INTO #t values (1, 1,   '2017-12-11', '2018-01-16'),   (1, 22,  '2018-01-28', '2018-03-9' ), (1, 333, '2018-03-1', '2018-12-31') ,  -- island
 (22, 1,  '2017-12-31', '2018-01-11'),   (22, 2,  '2017-2-11',  '2019-12-31')

declare @From Datetime='2018-01-01'
declare @To datetime='2018-12-31'

;with CTE as
(
select CustID,min(StartDD)StartDD
,max(EndDD)EndDD
from #t
group by CustID
)
select CustID,StartDD
,EndDD
from CTE
where StartDD<=@From and EndDD>=@To

此脚本未针对所有示例数据进行测试。 但是逻辑很清楚,可以相应地改正。

因此,请说明它不工作的样本数据。

【讨论】:

  • 谢谢你和大家
猜你喜欢
  • 1970-01-01
  • 2018-06-15
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 2019-01-02
  • 1970-01-01
  • 1970-01-01
  • 2014-10-02
相关资源
最近更新 更多