1

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

积分表如下所示:

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。

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

4

2 回答 2

4

不幸的是,您的表结构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

于 2013-03-25T19:44:06.647 回答
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
于 2013-03-25T19:00:13.497 回答