我有一个数据集,其中包括一堆客户和他们“停留”的日期范围。例如:
| ClientID | DateStart | DateEnd |
+----------+-----------+---------+
| 1 | Jan 1 | Jan 31 | (datediff = 30)
| 1 | Apr 4 | May 4 | (datediff = 30)
| 2 | Jan 3 | Feb 27 | (datediff = 55)
| 3 | Jan 1 | Jan 7 | (datediff = 6)
| 3 | Jan 10 | Jan 17 | (datediff = 6)
| 3 | Jan 20 | Jan 27 | (datediff = 6)
| 3 | Feb 1 | Feb 7 | (datediff = 6)
| 3 | Feb 10 | Feb 17 | (datediff = 6)
| 3 | Feb 20 | Feb 27 | (datediff = 6)
我的最终目标是能够识别客户通过阈值的日期 N
过去的夜晚 X
时间。比如说 30
最近几天 90
天。我还需要知道他们什么时候通过门槛。用例:酒店住宿和vip状态。
在上面的例子中,客户1在1月31日通过了阈值(在过去90天中有30个晚上),直到4月2日仍然保持达到阈值(现在在过去90天中只有29个晚上),但是在5月4日再次通过了阈值。
客户端2在2月3日通过了阈值,并一直达到阈值,直到4月28日,在这一点上最早的日子是90多天前,他们到期。
客户3在2月17日左右通过了门槛
所以我想生成一个这样的表:
| ClientID | VIPStart | VIPEnd |
+----------+-----------+---------+
| 1 | Jan 31 | Apr 2 |
| 1 | May 4 | Jul 5 |
| 2 | Feb 3 | Apr 28 |
| 3 | Feb 17 | Apr 11 |
(Forgive me if the dates are slightly off, I'm doing this in my head)
理想情况下,我想生成一个视图,因为我需要经常引用它。
我想知道最有效的方法是什么?假设我有成千上万的客户和成百上千的客人。
到目前为止,我处理这个问题的方法是使用一个包含参数的sql语句:as of {?Date}
,谁有vip身份谁没有。我是通过计算 DATEADD(day,-90,{?Date})
,然后排除超出范围的记录,然后截断 DateStart
越早越好 DateEnd
然后,计算 DATEDIFF(day,DateStart,DateEnd)
使用调整后的 DateStart
以及 DateEnd
,然后得到一个 SUM()
结果的 DATEDIFF()
对于每个客户 {?Date}
. 很管用,但不好看。它给了我一个时间点快照;我想要历史。
生成一个日期表,然后对每个日期使用上面的方法似乎有点低效。
我考虑的另一个选择是将原始数据转换成一个分解表,每个记录对应一个晚上,这样我可以更容易地计算。这样地:
| ClientID | StayDate |
+----------+-----------+
| 1 | Jan 1 |
| 1 | Jan 2 |
| 1 | Jan 3 |
| 1 | Jan 4 |
etc.
然后我可以添加一个列来计算过去90天中的天数,这样我就可以大致了解情况了。
但我不知道如何做到这一点。我有一个代码片段,可以这样做:
WITH DaysTally AS (
SELECT MAX(DATEDIFF(day, DateStart, DateEnd)) - 1 AS Tally
FROM Stays
UNION ALL
SELECT Tally - 1 AS Expr1
FROM DaysTally AS DaysTally_1
WHERE (Tally - 1 >= 0))
SELECT t.ClientID,
DATEADD(day, c.Tally, t.DateStart) AS "StayDate"
FROM Stays AS t
INNER JOIN DaysTally AS c ON
DATEDIFF(day, t.DateStart, t.DateEnd) - 1 >= c.Tally
OPTION (MAXRECURSION 0)
但我不能让它工作没有 MAXRECURSION
我认为你不能用 MAXRECURSION
现在我在闲逛。所以我要寻求的帮助是:追求目标最有效的方法是什么?如果你有一个代码示例,那也会很有帮助!谢谢。
2条答案
按热度按时间xjreopfe1#
可以通过在视图中创建计数表来消除递归。方法如下:
对于每个时段,生成从时段前90天到时段后90天的日期。这些都是该时期可能影响的“候选人日”。
对于每一行,添加一个标志,表明它是否在期间内(与之前和之后的90天相反)。
按客户id和日期聚合。
使用一个运行总和来获得前90天中30+的天数。
然后筛选30天以上的时间,并将其视为一个缺口和孤岛问题。
假设1000天就足够了(包括之前和之后的90天),那么查询如下所示:
没有递归的cte(尽管有一个可以用于
n
),这不受maxrecursion
问题。这是一把小提琴。
结果与你的结果略有不同。这可能是由于定义上的一些细微差别。以上包括作为“占用”日的结束日。在上述查询中,90天是前89天加上当天。倒数第二个查询显示了90天的运行天数,这在我看来是正确的。
xmq68pz92#
这是一个有趣的问题。首先,我将列举从每个客户第一次入住开始到最后一次入住结束后90天的天数。然后,您可以将stay表与left联接起来,并使用窗口函数来标记“vip”天数(请注意,这假设给定客户机没有重叠的停留,这与示例数据一致)。
接下来是间隙和孤岛:您可以使用窗口总和将“相邻的”vip天数分组,然后进行聚合。
db上的这个演示会处理您的示例数据,产生:
您可以将其置于如下视图中:
这个
order by
以及option(maxrecursion)
创建视图时必须省略子句每一个在其
from
子句必须以结尾option(max recursion 0)
演示