如何在灵活的时间段内找到每日差异?

时间:2014-07-14 06:07:43

标签: sql sql-server olap-cube

我有一组非常详细的数据:

CustomerId     char(6)
Points         int
PointsDate     date

使用示例数据,例如:

000021   0   01-JAN-2014
000021  10   02-JAN-2014
000021  20   03-JAN-2014
000021  30   06-JAN-2014
000021  40   07-JAN-2014
000021  10   12-JAN-2014
000034   0   04-JAN-2014
000034  40   05-JAN-2014
000034  20   06-JAN-2014
000034  40   08-JAN-2014
000034  60   10-JAN-2014
000034  80   21-JAN-2014
000034  10   22-JAN-2014

因此,PointsDate组件不一致,也不连续(它基于某些"活动"发生)

我试图为每个客户获得积分和积极差异的总量,正负变化的数量,以及最大值和最小值...但是忽略了客户的第一个实例 - 总是零。

e.g。

CustomerId  Pos  Neg  Count(pos) Count(neg) Max  Min
000021      40   30           3          1   40   10
000034     100   90           4          2   80   10

...但我没有一个线索如何实现这个目标!

我会把它放在一个立方体中,但是a)只有一个表而没有其他参考文献和b)我对立方体几乎一无所知!

3 个答案:

答案 0 :(得分:3)

问题可以在常规TSQL中解决,其中包含一个公共表表达式,该表达式对每个客户的行进行编号,以及将每行与前一行进行比较的外部自联接;

WITH cte AS (
  SELECT customerid, points, 
    ROW_NUMBER() OVER (PARTITION BY customerid ORDER BY pointsdate) rn
  FROM mytable
)
SELECT cte.customerid, 
  SUM(CASE WHEN cte.points > old.points THEN cte.points - old.points ELSE 0 END) pos,  
  SUM(CASE WHEN cte.points < old.points THEN old.points - cte.points ELSE 0 END) neg,
  SUM(CASE WHEN cte.points > old.points THEN 1 ELSE 0 END) [Count(pos)],  
  SUM(CASE WHEN cte.points < old.points THEN 1 ELSE 0 END) [Count(neg)],
  MAX(cte.points) max, 
  MIN(cte.points) min
FROM cte
JOIN cte old
  ON cte.rn = old.rn + 1
 AND cte.customerid = old.customerid
GROUP BY cte.customerid

An SQLfiddle to test with

使用SQL Server 2012更广泛的分析功能可以稍微简化查询。

答案 1 :(得分:1)

类似于Joachim Isaksson的方法,但在CTE中有更多工作,而在主要查询上则更少

WITH A AS (
  SELECT c.CustomerID, c.Points, c.PointsDate
       , Diff = c.Points - l.Points
       , l.PointsDate lPointsDate
  FROM   Customer c
         CROSS APPLY (SELECT TOP 1
                             Points, PointsDate
                      FROM   Customer cu
                      WHERE  c.CustomerID = cu.CustomerID
                        AND  c.PointsDate > cu.PointsDate
                      ORDER BY cu.PointsDate Desc) l
)
SELECT CustomerID
     , Pos = SUM(Diff * CAST(Sign(Diff) + 1 AS BIT))
     , Neg = SUM(Diff * (1 - CAST(Sign(Diff) + 1 AS BIT)))
     , [Count(pos)] = SUM(0 + CAST(Sign(Diff) + 1 AS BIT))
     , [Count(neg)] = SUM(1 - CAST(Sign(Diff) + 1 AS BIT))
     , Max(Points) [Max], Min(Points) [Min]
FROM   A
GROUP BY CustomerID

SQLFiddle Demo

删除第一天的条件是JOIN中的CROSS APPLYCTE):前一天没有前一天,因此会被过滤掉。

在主要查询中,我不是使用CASE来过滤正面和负面的差异,而是首选SIGN函数:

  • 此函数返回-1表示负数,0表示零,+1表示正数
  • 使用Sign(Diff) + 1移动值意味着新的返回值为0,1和2
  • CAST位压缩为0为负,1为零或为正。

0 +定义中的[Count(pos)]创建隐式转换为整数值BIT无法求和。
1 -SUMCOUNT的负差异相当于NOT:它将BIT SIGN的值反转为1表示负数,0表示零表示积极的。

答案 2 :(得分:0)

我将从上面复制我的评论:我对立方体一无所知,但听起来你正在寻找的只是一个光标,不是吗?我知道每个人都讨厌游标,但这是我知道比较连续行而不将其加载到客户机上的最佳方式(这显然更糟)。

我看到你在回复我时提到你可以将它设置为隔夜运行,所以如果你愿意接受这种表现,我绝对认为光标将是最容易和最快的。实行。如果这只是你在这里或那里做的事情,我肯定会这样做。这是没有人最喜欢的解决方案,但它可以完成任务。

不幸的是,是的,在1200万条记录中,你肯定会花一些时间来优化你的光标。我经常使用一个大小相同的数据库,我只能想象它需要多长时间。虽然根据您的使用情况,您可能希望根据用户进行过滤,在这种情况下,光标将更容易编写,我怀疑您将面对足够的记录来导致很多问题。例如,您可以查看前20位用户并测试他们的记录,然后根据需要执行更多操作。