【问题标题】:How to sum cumulative days an account is active from a table with multiple date ranges per account?如何从每个帐户具有多个日期范围的表中汇总帐户活跃的累积天数?
【发布时间】:2019-08-20 22:32:09
【问题描述】:

我有一个交易表,其中包含帐户、TN 号、供应状态、交易状态和日期。如果一个帐户至少有一个成功配置的 TN 号码且没有取消配置状态,则该帐户被视为有效。

每个帐户可以在一段时间内处于活动状态,然后取消配置最后一个活动的 TN,它就会变为非活动状态。但如果 aTN 配置成功,该帐户可以再次处于活动状态。

我需要将帐户活动的累计天数与至少一个处于预置状态的 TN 相加。

这是我的事务表的示例。

ACCOUNT TN_NUMBER   STATUS      TRANSACTION_STATUS  DATE
------- ---------   --------------  ------------------  ----------
1234    8005551212  Provisioned     Success     2019-05-17
1234    8665558989  Provisioned     Success     2019-05-25
1234    8005551212  De-provisioned  Success     2019-05-27
1234    8665558989  De-provisioned  Failed      2019-06-03
1234    8665558989  De-provisioned  Success     2019-06-05
1234    8005551212  Provisioned     Success     2019-06-01
5678    8005557777  Provisioned     Success     2019-01-01
5678    8005557777  De-provisioned  Success     2019-05-01

帐户 1234 于 2019 年 5 月 17 日开始,于 2019 年 6 月 5 日取消配置该帐户的最后一个 TN。 (14 天有效) 然后该帐户从 2019 年 6 月 1 日开始再次处于活动状态并保持活动状态。 (61 天有效)。

帐户 5678 活跃了 4 天。

这需要每天查询 170 万个帐户。

【问题讨论】:

  • 酷。感谢您提供示例数据,这非常有帮助!对你的第一篇文章来说还不错!您能否展示您尝试过的内容、遇到的问题以及您的代码遇到的任何错误?
  • 你想要什么结果!

标签: sql sql-server tsql


【解决方案1】:

使用累积和来获取每个时间点的预置帐户数。然后通过计算未配置帐户的数量少于每行来分配“分组”:

select t.*,
       sum(case when num_provisioned <= 0 then 1 else 0 end) over (partition by account order by date) as grouping
from (select t.*,
             sum(case when transaction_status = 'Success' and status = 'Provisioned'
                      then 1
                      when transaction_status = 'Success' and status = 'Provisioned'
                      then 1
                      when transaction_status = 'Success' and status = 'Unprovisioned'
                      then -1
                      else 0
                 end) over (partition by account order by date) as num_provisioned
      from t
     ) t

有了这些信息,接下来就是几个聚合和lead()(进行下一次取消配置)的问题:

with g as (
      select t.*,
             sum(case when num_provisioned <= 0 then 1 else 0 end) over (partition by account order by date) as grouping
      from (select t.*,
                   sum(case when transaction_status = 'Success' and status = 'Provisioned'
                            then 1
                            when transaction_status = 'Success' and status = 'Provisioned'
                            then 1
                            when transaction_status = 'Success' and status = 'Unprovisioned'
                            then -1
                            else 0
                       end) over (partition by account order by date) as num_provisioned
            from t
           ) t
      )
select account,
       sum(datediff(day, min_date, coalesce(max_date, getdate()))) as num_days
from (select account, grouping, max(num_provisioned) as num_provisioned,
             min(date) as min_date, max(date) as max_date,
             lead(min(date)) over (partition by account order by min(date)) as next_min_date
      from g
      group by account, grouping
     ) g
where num_provisioned > 0
group by account;

【讨论】:

    【解决方案2】:

    你可以使用简单的聚合:

    select  t1.Account,
            sum(datediff(day,t1.[date],t2.[date])) as NumDays
    from #t1 as t1
        inner join #t1 as t2 ON t1.Account = t2.Account and t1.TN_NUMBER = t2.TN_NUMBER
    where t1.status = 'Provisioned'
      and t1.TRANSACTION_STATUS = 'Success'
      and t2.status = 'De-provisioned'
      and t2.TRANSACTION_STATUS = 'Success'
      and t1.[Date] <= t2.[Date]
    group by t1.Account
    

    【讨论】:

      【解决方案3】:

      您能否检查您的问题:2019-06-05 是在 2019-06-01 之后,因此从 2019-05-17 到今天,帐户 1234 的配置没有中断,2019 年之间有 4 个月(不是天)- 01-01 和 2019-05-01。

      这是我的脚本。如果你有很多数据,我认为它会更快。

      WITH PRO AS (
          SELECT *
          FROM DBO.[TRANSACTION]
          WHERE STATUS = 'Provisioned'
                  AND TRANSACTION_STATUS = 'Success' ), 
      DEPRO AS (
          SELECT *
          FROM DBO.[TRANSACTION]
          WHERE STATUS = 'De-provisioned'
                  AND TRANSACTION_STATUS = 'Success' ), 
      -- Create interval for transactions
      INTERVAL AS (
          SELECT T1.ACCOUNT, T1.DATE AS BEGIN_DATE, ISNULL(T2.DATE,CONVERT (DATE, GETDATE())) AS END_DATE
          FROM PRO T1
          LEFT JOIN DEPRO T2
              ON T1.TN_NUMBER = T2.TN_NUMBER
                  AND T1.DATE < T2.DATE ) ,
      -- Create concatenated intervals 
      INTERVAL_CONCAT (ACCOUNT, BEGIN_DATE, END_DATE) AS (
          SELECT ACCOUNT,BEGIN_DATE, END_DATE
          FROM INTERVAL
          UNION ALL
          SELECT I1.ACCOUNT, I2.BEGIN_DATE, I1.END_DATE
          FROM INTERVAL I1
          INNER JOIN INTERVAL_CONCAT I2
              ON I1.ACCOUNT = I2.ACCOUNT
                  AND I1.BEGIN_DATE < I2.END_DATE
                  AND I1.END_DATE > I2.END_DATE ),
      -- Remove duplicates 
      INTERVAL_RN AS (
          SELECT *, RN=ROW_NUMBER()OVER(PARTITION BY ACCOUNT, BEGIN_DATE, END_DATE ORDER BY  ACCOUNT)
          FROM INTERVAL_CONCAT ), 
      INTERVAL_WO_DUP AS (
          SELECT ACCOUNT, BEGIN_DATE, END_DATE
          FROM INTERVAL_RN
          WHERE RN=1 ),
      -- Remove underlying intervals 
      INTERVAL_CLEANING AS ( 
          SELECT *
          FROM INTERVAL_WO_DUP 
          EXCEPT
          SELECT I2.*
          FROM INTERVAL_WO_DUP I1
          INNER JOIN INTERVAL_WO_DUP I2
              ON I2.BEGIN_DATE < I1.END_DATE
                  AND I2.BEGIN_DATE>= I1.BEGIN_DATE
                  AND I2.END_DATE <=I1.END_DATE
                  AND NOT (I2.BEGIN_DATE = I1.BEGIN_DATE AND I2.END_DATE=I1.END_DATE))
      
      SELECT ACCOUNT, SUM(DATEDIFF(D,BEGIN_DATE,END_DATE)) AS PROVISIONED_DURATION
      FROM INTERVAL_CLEANING
      GROUP BY  ACCOUNT
      

      包含您的数据的结果集

      ACCOUNT PROVISIONED_DURATION
      ------- --------------------
      1234    95
      5678    120
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多