【问题标题】:SQL Rolling Total up to a certain dateSQL Rolling Total 到某个日期
【发布时间】:2013-03-25 18:50:39
【问题描述】:

我正在使用两张桌子。我们称他们为“客户”和“积分”。

积分表如下所示:

Account Year M01 M02 M03 M04 M05 M06 M07 M08 M09 M10 M11 M12
123     2011  10   0   0   0  10   0  10   0   0   0   0  10
123     2012   0   0   0   0  10   0   0  10  10  10  10  20
123     2013   5   0   0   0   0   0   0   0   0   0   0   0

但这些点在滚动 12 个月内有效。计算当前客户的积分很简单,但挑战在于不再活跃的客户。假设客户 123 在 2013 年 1 月变得不活动,我们只想计算 2013 年 2 月至 2013 年 1 月。这就是另一个表 Customers 的用武之地,让我们简化并说它看起来像这样:

Account   End Date
123       20130105

现在,我要做的是创建一个查询来计算每个客户拥有的积分数量。 (当前 12 个月适用于活跃客户,过去 12 个月适用于不再活跃的客户。)

这里有更多信息:

  • 我正在运行 SQL Server 2008。
  • 这些表格是这样提供给我的,我无法修改它们。
  • 活跃客户是指结束日期为 99991231(9999 年 12 月 31 日)的客户
  • 积分表仅填充客户是活跃客户的年份。 Aka,某人在 2009 年 2 月成为活跃客户,他们有 2009 年的条目,如果他们在 2009 年 7 月变得不活跃,他们的积分仅计算 2009 年 2 月至 7 月,2008 年没有行,因为他们不是客户那时。 2009 年 1 月和 8 月至 12 月将显示 0。
  • 此外,仅当客户当年获得任何积分时才会创建记录。如果客户在一年内获得 0 分,则不会有记录。
  • 对于边境案件,如果您进入一个月的第一天,则计算该月。例如,假设今天是 2013 年 4 月 1 日,这意味着我们总结了 May'12-April'13。

这是一个相当复杂的问题。如果有什么我可以更好地解释的,请告诉我。谢谢!

【问题讨论】:

    标签: sql sql-server-2008


    【解决方案1】:

    不幸的是,您的表结构为points,您将不得不取消透视数据。 unpivot 将数据从多列中提取到行中。一旦数据在行中,加入、过滤数据和合计每个帐户的积分就会容易得多。取消透视数据的代码将与此类似:

    select account,
      cast(cast(year as varchar(4))+'-'+replace(month_col, 'M', '')+'-01' as date) full_date,
      pts
    from points
    unpivot
    (
      pts
      for month_col in ([M01], [M02], [M03], [M04], [M05], [M06], [M07], [M08], [M09], [M10], [M11], [M12])
    ) unpiv
    

    SQL Fiddle with Demo。查询给出的结果类似于:

    | ACCOUNT |  FULL_DATE | PTS |
    ------------------------------
    |     123 | 2011-01-01 |  10 |
    |     123 | 2011-02-01 |   0 |
    |     123 | 2011-03-01 |   0 |
    |     123 | 2011-04-01 |   0 |
    |     123 | 2011-05-01 |  10 |
    

    一旦数据采用这种格式,就可以加入Customers表,得到每个account的总分,所以代码类似如下:

    select 
      c.account, sum(pts) TotalPoints
    from customers c
    inner join 
    (
      select account,
          cast(cast(year as varchar(4))+'-'+replace(month_col, 'M', '')+'-01' as date) full_date,
        pts
      from points
      unpivot
      (
        pts
        for month_col in ([M01], [M02], [M03], [M04], [M05], [M06], [M07], [M08], [M09], [M10], [M11], [M12])
      ) unpiv
    ) p
      on c.account = p.account
    where 
    (
      c.enddate = '9999-12-31'
      and full_date >= dateadd(year, -1, getdate()) 
      and full_date <= getdate()  
    )
    or
    (
      c.enddate <> '9999-12-31'
      and dateadd(year, -1, [enddate]) <= full_date
      and full_date <= [enddate]
    )
    group by c.account
    

    SQL Fiddle with Demo

    【讨论】:

      【解决方案2】:

      糟糕的数据结构。首先要做的是取消旋转它。然后你会得到一个以年-月-点为列的表格。

      从这里,您可以只选择最近的 12 个月。事实上,您甚至不必担心客户何时离开,因为大概他们从那时起就没有收集积分。

      这是一个 SQL 示例:

      with points as (
          select 123 as account, 2012 as year,
                 10 as m01, 0 as m02, 0 as m03, 0 as m04, 10 as m05, 0 as m06,
                 10 as m07, 0 as m08, 0 as m09, 0 as m10, 0 as m11, 10 as m12
         ),
         points_ym as (
          select account, YEAR, mon, cast(right(mon, 2) as int) as monnum, points
          from points
          unpivot (points for mon in (m01, m02, m03, m04, m05, m06, m07, m08, m09, m10, m11, m12)
          ) as unpvt
         )
      select account, SUM(points)
      from points_ym
      where year*12+monnum >= year(getdate())*12+MONTH(getdate()) - 12
      group by account
      

      【讨论】:

      • 糟糕的数据结构,确实,完全同意。感谢您的回复。
      猜你喜欢
      • 1970-01-01
      • 2016-06-21
      • 1970-01-01
      • 2012-03-03
      • 2022-06-13
      • 2016-11-02
      • 2016-08-08
      • 1970-01-01
      相关资源
      最近更新 更多