【问题标题】:Count multiple repeats after event as single repeat将事件后的多次重复计数为单次重复
【发布时间】:2014-05-20 20:13:34
【问题描述】:

我想要做的是提出一个查询,该查询可以给出初始事件后 30 天内重复的百分比,但仅将 30 天内的任何事件计为一次重复。这是一个人的样本数据集:

人员      日期
══════════════
一个               2014 年 3 月 1 日
一个               2014 年 3 月 21 日
一个               2014 年 3 月 29 日
一个               2014 年 4 月 14 日
一个               2014 年 4 月 17 日

在这种情况下,3/21 将是重复事件,而 3/29 不会被计为一秒。 4/14 将是下一个窗口的开始,4/17 是第二次重复。

这里要计算重复的百分比,分子是当月发生初始事件并且在 30 天内发生后续事件的人的不同计数。分母是当月发生事件的人的不同数量。在跨月的情况下,重复计算在初始事件的月份内。

我知道我可以想出一些使用循环/光标或临时表的东西,但是随着数据集的增长,这将需要很长时间。有人对如何将其作为单个查询执行此操作有任何想法吗?它可能会涉及到几个 CTE。到目前为止我想出的一切都失败了。

【问题讨论】:

  • 对我来说,很难掌握你想要做什么。什么是初始事件?什么是重复?什么是跨月?
  • 将初始事件视为启动时钟。从那里开始,如果在 30 天内有另一个条目,则视为重复。问题是,如果在 30 天内有多个条目,则在这 30 天结束之前,它们不会算作额外的重复,也不会重新开始计时。跨越一个月意味着为了计算的目的,初始事件的月份是您计算重复的月份(即,如果在 3/30 和 4/2 有一个事件,那么重复将计入 3 月和另一个初始事件要到 4/30 才能发生)。如果您还需要我进一步澄清,请告诉我。

标签: sql sql-server tsql sql-server-2008-r2


【解决方案1】:

不错的...试试这个:

create table #t (Person varchar(10), EventDate date);
insert #t (Person, EventDate)
values
('A', '3/1/14'),
('A', '3/21/14'),
('A', '3/29/14'),
('A', '4/14/14'),
('A', '4/17/14'),
('A', '8/3/14'),
('B', '3/25/14'),
('B', '4/2/14'),
('B', '4/20/14'),
('B', '6/14/14'),
('B', '8/17/14'),
('B', '8/26/14');
;WITH OrderedEvents AS (
    SELECT  Person, EventDate, ROW_NUMBER() OVER (PARTITION BY Person ORDER BY EventDate) AS Ord
    FROM    #t
)
, RepeatedEvents AS (
    SELECT  Person, EventDate, Ord, EventDate AS InitialDate
    FROM    OrderedEvents
    WHERE   Ord = 1
    UNION ALL
    SELECT  o.Person, o.EventDate, o.Ord
            , CASE WHEN DATEDIFF(DAY, r.InitialDate, o.EventDate) > 30 THEN o.EventDate ELSE r.InitialDate END
    FROM    OrderedEvents o
            JOIN RepeatedEvents r ON o.Person = r.Person AND o.Ord = r.Ord + 1
)
, GroupedEvents AS (
    SELECT  Person, MONTH(InitialDate) AS Mth, YEAR(InitialDate) AS Yr
            , IsRepeat = CASE WHEN COUNT(*) > 1 THEN 1 ELSE 0 END
    FROM    RepeatedEvents
    GROUP BY Person, MONTH(InitialDate), YEAR(InitialDate)
)
SELECT  Mth, Yr, CAST(SUM(IsRepeat) AS NUMERIC) / CAST(COUNT(DISTINCT person) AS NUMERIC) AS Pct
FROM    GroupedEvents 
GROUP BY Mth, Yr;

【讨论】:

  • 感谢您的帮助。我以前与此类似的问题计算了每次重复,因此要容易得多。你有什么想法让它更快吗?我在 row_number 中排序的值上放置了一个聚集索引,这有很大帮助。我的实际数据有 30k 多行,执行此查询需要 10 到 15 分钟。
  • 不客气。使用递归 CTE 是 Jeff Moden 所说的 RBAR(逐行痛苦)。这并不是真正的基于集合的操作。在这种情况下,这很棘手,因为实际上您需要像进行运行总计计算一样扫描每一行。如果你想要一个能让这个绝对尖叫的技巧,请查看“Quirky Update”,我很确定这也是 Jeff Moden 的想法之一(或者至少他做了很多宣传它)。我 99% 确信该技术可以解决您的问题,并且可能会在几秒钟内运行。
  • 递归本身我很好。问题是它每次都在执行第一个 CTE。我试图通过在旧版本的 SQL Server 中工作的 TOP 技巧或提示想出一种方法,但没有任何效果。我所做的是让第一个 CTE 成为临时表,然后它运行大约 1 秒而不是 9 分钟。我想避免使用临时表(将其设置为视图),但有时,这就是生活。
  • 很棒的优化,肖恩。感谢您的跟进。
猜你喜欢
  • 1970-01-01
  • 2020-11-07
  • 1970-01-01
  • 1970-01-01
  • 2016-06-16
  • 2019-11-14
  • 2020-11-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多