【问题标题】:SQL - Counting and Summarizing (in ranges) the number of instances of a given transactionSQL - 计算和汇总(在范围内)给定事务的实例数
【发布时间】:2011-01-09 06:35:48
【问题描述】:

我有一个 Occurrences 表,其中包含用户每次执行操作时的一行。用户每天多次执行一个操作。它看起来像这样:

Date      Username
------    --------
1/1/9     User1
1/1/9     User1
1/1/9     User2
1/2/9     User1
1/2/9     User3
1/3/9     User1
1/3/9     User1
1/3/9     User1
1/3/9     User2
1/3/9     User3
1/4/9     User1
1/5/9     User1
1/6/9     User1
1/7/9     User1

对于该范围内的每一天,我想显示多次执行该操作的人数 - 例如 2 到 5 次、6 到 10 次以及 10 次以上。但是,我考虑在不同的日子重复该操作以计为该操作的另一个实例。例如,如果用户在第一天做了 3 次,然后在第二天再做任何 # 次,我看到用户已经做了 2 次操作,因此应该在 2 到 5 次列。

上述样本数据对应的结果集为:

        #_of_people       #_of_people       #_of_people       
        who_did_action    who_did_action    who_did_action        
Date    2to5_times        6to10_times       more_than_10      Total
-----   --------------    --------------    --------------    -----
1/1/9   0                 0                 0                 0
1/2/9   1                 0                 0                 1
1/3/9   3                 0                 0                 3   
...    
1/7/9   0                 1                 0                 1        

请注意,结果的每一行都在计算重复操作的次数仅针对特定日期 - 不是累积的。

  • 1/1/9 行全为零,因为这是第一天,所有操作都被视为第一天。
  • 1/2/9 行是 1、0、0、1,因为只有 User1 重复了 - 这是 User3 的第一次。
  • 1/3/9 行是 3,0,0,3 因为 User1 重复了两次,User2 重复了一次,User3 也重复了一次。
  • 1/7/9 行是 0、1、0、1,因为 User1 重复了 6 次。

【问题讨论】:

  • 日期是日期时间数据类型。但如果有必要,我很乐意在任何事物之间进行转换。 :)
  • 在这种情况下是 2008 年,但确实如此。如果可能,更愿意使用非特定于版本的功能,但如果没有,请不要担心。
  • 用户 1 如何从 1/3/9 重复两次到 1/4/9 重复 6 次?也许我很密集,但我无法理解你的数字。
  • @Tom H.:我相信表格只是日志。
  • @Tom - 不确定您是否看到我的最后一条评论,但我刚刚删除了它。你是绝对正确的 - 我的意思是说“1/7/9”行是 0、1、0、1。我已经进行了更正。感谢您指出这一点。

标签: sql sql-server tsql aggregate pivot


【解决方案1】:

我能想到的最好的,未经测试:

DECLARE @username VARCHAR(40)
DECLARE @date DATETIME
DECLARE @counter INT
    SET @counter = 1

WITH occur AS (
     SELECT DISTINCT
            CONVERT(VARCHAR(10), o.date, 101) AS dt,
            o.username
       FROM OCCURRENCES o
   ORDER BY o.username, dt),
     occur_rank AS (
     SELECT x.dt,
            @username = x.username,
            @date = CAST(x.dt AS DATETIME),
            CASE WHEN @username = x.username AND @date + 1 = x.date THEN @counter = @counter + 1 ELSE @counter = 1 END AS rank
       FROM occur x
   ORDER BY x.username, x.dt)
  SELECT or.dt,
         SUM(CASE WHEN or.rank BETWEEN 2 AND 5 THEN 1 ELSE 0 END) AS 2_to_5,
         SUM(CASE WHEN or.rank BETWEEN 6 AND 10 THEN 1 ELSE 0 END) AS 6_to_10
    FROM occur_rank or
GROUP BY or.dt

如果您可以在日期间隔上重置编号,剩下的就很容易了。但是none of the SQL Server ranking functions support that。这样就剩下手动递增变量了。

第一个 CTE,occur 只返回与日期关联的用户名列表。第二个 CTE 以它为基础,为最终输出添加排名。

【讨论】:

  • 转换是去除DATE 列的时间部分所必需的,因为它是DATETIME 数据类型。
  • 你读懂了我的想法。 :) 我现在正在尝试这个。我相信在“as dt”和 COUNT(*) 和“num_occur”之间应该有一个逗号,对吧?
  • @Robert:修正了缺少的逗号,谢谢。 AS 不是定义表别名所必需的,但我应该保持一致。
  • 不要对两个结果使用 Case,而是使用 IIF 函数。它更整洁。
  • 但是“num_occur”在这里用作列别名,而不是我认为的表别名?
【解决方案2】:

我使用了带有相关子查询的派生表。

相关的子查询会生成一个 ActionCount 列,该列计算直到该行的用户名的该行的日期(包括该行的日期)为止的不同日期的数量。

生成的派生表列出了每个日期、用户名和操作计数。

然后,外部查询按日期对结果进行分组,计算 2 到 5 之间、6 到 10 之间、大于 10 的 ActionCount 的数量,并且对于 Total,大于 1(ActionCount 等于 1 不是'重复')。

select
    [Date],
    sum(case when [ActionCount] between 2 and 5 then 1 else 0 end) [#_of_people_who_did_action_2to5_times],
    sum(case when [ActionCount] between 6 and 10 then 1 else 0 end) [#_of_people_who_did_action_6to10_times],
    sum(case when [ActionCount] > 10 then 1 else 0 end) [#_of_people_who_did_action_more_than_10],
    sum(case when [ActionCount] > 1 then 1 else 0 end) [Total]
from (
    select distinct
        dt.[Date],
        dt.[Username],
        (select count(distinct [Date]) from OCCURRENCES cd where cd.[Username] = dt.[Username] and cd.[Date] <= dt.[Date]) [ActionCount]
    from OCCURRENCES dt
) a
group by [Date]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-05
    • 1970-01-01
    相关资源
    最近更新 更多