在SQL中的一段时间内对值进行求和(“滚动窗口”)

时间:2016-10-13 20:40:35

标签: sql sql-server

假设我有以下数据:

ContactDate Zip   
10-1-2016   90210
10-7-2016   90211
10-8-2016   90210
10-8-2016   90211

我想总结一下30天内每个拉链发生的联系人数量。然而,它不是按月,而是用户的月份结算日。那么就说那天是每个月的第二天......

我正在尝试获得以下结果:

BillingMonthStart Zip   Count
9-2-2016          90210 1
10-2-2016         90210 1
10-2-2016         90211 2

这是我的尝试(显然表结构不同,我将开始修复上面的例子):

select 
    C1.ContactDate, count(C1.ContactDate)
from 
    dbo.contacts c1 join dbo.contacts C2 on 
        C2.contactdate between C1.ContactDate AND dateadd(day, 30, C1.ContactDate)
        AND c1.account = c2.accountid
    join payment.dbo.AccountBilling B  on c1.accountid = B.AccountID
where 
    c1.account = @accountid
    and C1.ContactDate >= @start_date
    and C1.ContactDate <= @end_date
    and DAY(C1.ContactDate) = B.BillingDOM
Group by C1.contactDate

这里的问题是,如果该帐户确实在结算日获得了联系,那么我将无法获得该月的结果。

修改 首先,对于形成不良的问题感到抱歉。我已经弄明白了(虽然我确信有更好的方法)。这是我做的:

  1. 以变量
  2. 获取结算月份
  3. 使用递归CTE我生成了所有结算日期(在我关心的范围内)
  4. 使用between来自#2和billingDate + 1个月的billingDate加入联系人数据表
  5. 按结算日期分组并选择 计数。
  6. 这是SQL:

    DECLARE @start_date DateTime
    DECLARE @end_date DateTime
    DECLARE @accountid int
    DECLARE @billing_dom tinyint
    
    SET @start_date = '2016-01-01' 
    SET @end_date = '2016-12-31'
    SET @accountid = 6045032;
    
    select @billing_dom = BillingDOM
    from Payment.dbo.AccountBilling
    where AccountID = @accountid;
    
    
    WITH cte_months AS
    (
        SELECT
            BillingDate = CAST(CAST(datepart(yyyy, @start_date) AS varchar) + '-' + CAST(datepart(mm, @start_date) AS varchar) + '-' + CAST(@billing_dom AS varchar) AS DATETIME),
            dt = @start_date
        UNION ALL
        SELECT
            BillingDate = DateAdd(mm, 1, BillingDate),
            dt = DATEADD(mm, 1, dt)
        FROM
            cte_months
        WHERE
            dt < DATEADD(mm, -1, @end_date)
    )
    --SELECT * FROM cte_months
    
    
    select 
        cte_months.BillingDate, count(C.ContactDate) as Contacts
    from 
        cte_months left join dbo.contacts C on 
            C.ContactDate between cte_months.BillingDate and dateadd(mm, 1, cte_months.BillingDate)
    where 
        C.accountid = @accountid
    Group by cte_months.BillingDate
    

2 个答案:

答案 0 :(得分:0)

以下是如何实现此目的的示例,使用案例陈述按结算周期对数据进行分组。如果不确切知道你的表格是什么样子,很难确切地知道select语句应该是什么,但一般概念仍然适用于此:

DECLARE @myTable TABLE (ContactDate DATE, Zip INT);
INSERT @myTable VALUES ('10-1-2016', 90210), ('10-7-2016', 90211), ('10-8-2016', 90210), ('10-8-2016', 90211);

DECLARE @myBilling TABLE (Zip INT, BillingDay INT);
INSERT @myBilling VALUES (90210, 2), (90211, 2);

SELECT CAST(CASE WHEN DATEPART(DAY, T.ContactDate) < B.BillingDay 
            THEN DATEADD(DAY, B.BillingDay - 1, DATEADD(MONTH, DATEDIFF(MONTH, 0, T.ContactDate) - 1, 0))
            ELSE DATEADD(DAY, B.BillingDay - 1, DATEADD(MONTH, DATEDIFF(MONTH, 0, T.ContactDate), 0))
       END AS DATE) BillingPeriod
     , T.Zip
     , COUNT(*) theCount
FROM @myTable T
JOIN @myBilling B ON B.Zip = T.Zip  
GROUP BY T.Zip
        ,CASE WHEN DATEPART(DAY, T.ContactDate) < B.BillingDay 
              THEN DATEADD(DAY, B.BillingDay - 1, DATEADD(MONTH, DATEDIFF(MONTH, 0, T.ContactDate) - 1, 0))
              ELSE DATEADD(DAY, B.BillingDay - 1, DATEADD(MONTH, DATEDIFF(MONTH, 0, T.ContactDate), 0))
         END;

答案 1 :(得分:0)

您是按月分组(28至31天),还是总是30天?

如果它总是30天,那么除了任何其他非日期列之外,还要分组。

ceiling(cast(datediff(day,BillingMonthStart,ContactDate) AS FLOAT)/30)

这将为您提供BillingMonthStart和ContactDate四舍五入之间的30天间隔。也就是说,它在0天的同一天计算联系,包括第30天。如果您希望第30天成为新组,那么:

ceiling((cast(datediff(day,BillingMonthStart,ContactDate) AS FLOAT)+1)/30)

如果您希望按月持续时间而不是静态30天,那么:

datediff(month,@BillingMonthStart,@ContactDate) - CASE
        WHEN datepart(day,@BillingMonthStart) > datepart(day,@ContactDate) THEN 1
        ELSE 0
        END