Snowflake - 在使用窗框或订单时获取不同用户的计数

Posted

技术标签:

【中文标题】Snowflake - 在使用窗框或订单时获取不同用户的计数【英文标题】:Snowflake - Getting a Count of Distinct Users While Using Window Frame or an Order 【发布时间】:2020-03-25 16:48:33 【问题描述】:

我正在尝试编写一个查询来获取一个月内的累积用户数。

WITH USERS_PER_DAY AS (
  SELECT 
    DATE_TRUNC('day', HOUR_DIM.UTC) DAY
  , COUNT(DISTINCT CLIENT_SID) ACTIVE_USER_COUNT
  FROM RPT.S_HOURLY_INACTIVE_TVS_AGG
  WHERE DATEDIFF('month', HOUR_DIM.UTC, CURRENT_DATE) BETWEEN 0 AND 0
  GROUP BY 
    DATE_TRUNC('day', HOUR_DIM.UTC) 
)
SELECT  
DAY,
SUM(ACTIVE_USER_COUNT) OVER (PARTITION BY APP_NAME ORDER BY DAY ASC rows between unbounded preceding and current row) CUMULATIVE_ACTIVE_USER_ACOUNT
FROM USERS_PER_DAY

现在的输出如下所示:

问题是我需要计算当月的不同或唯一用户,但此查询包含天之间用户的重复。我知道我不能在我的窗口函数中使用 count(distinct ...),但是还有其他方法可以确保我在几天之间没有重复用户吗?

【问题讨论】:

【参考方案1】:

对此的“聪明”方法是使用dense_rank()s 的总和:

SELECT first_day, APP_NAME,
       SUM(COUNT(*)) OVER (PARTITION BY APP_NAME ORDER BY first_day ASC) as CUMULATIVE_ACTIVE_USER_ACOUNT
FROM (SELECT CLIENT_SID, APP_NAME,
             MIN(DATE_TRUNC('day', HOUR_DIM.UTC)) as first_day
      FROM RPT.S_HOURLY_INACTIVE_TVS_AGG
      WHERE DATEDIFF('month', HOUR_DIM.UTC, CURRENT_DATE) BETWEEN 0 AND 0
      GROUP BY CLIENT_SID, APP_NAME
     ) cs
GROUP BY first_day, APP_NAME;

【讨论】:

感谢您的回复。但是,这似乎对我不起作用。它会生成一个每天有 50 个用户的表。 @JamesD。 . . .我有错误的想法。我只是完全重写了答案。关键思想是获取每个用户的第一个日期,然后将其相加。 只要您每天至少有一个用户拥有他们的first_day,这个方法就可以工作。【参考方案2】:

因此,一个天真的解决方案是将数据转换为不同的日期和每天的不同用户,然后将它们加入 CTE 以获得结果:

WITH data AS (  
    select 
        hour_dim_utc::timestamp_ntz as hour_dim_utc
        ,user_id 
    from values
        ('2020-03-10 9:50', 1 ),
        ('2020-03-10 9:51', 3 ),
        ('2020-03-10 10:51', 3 ),
        ('2020-03-11 9:52', 1 ),
        ('2020-03-11 9:53', 2 ),
        ('2020-03-11 9:54', 0 ),
        ('2020-03-12 9:55', 0 ),
        ('2020-03-12 9:56', 1 ),
        ('2020-03-12 9:57', 3 ),
        ('2020-03-14 9:58', 2 ),
        ('2020-03-15 9:59', 3 ),
        ('2020-03-16 10:00', 2 ),
        ('2020-03-17 10:01', 2 ),
        ('2020-03-18 10:02', 0 ),
        ('2020-03-19 10:04', 11 )
         s( hour_dim_utc, user_id)
), distinct_users_days AS (
    select distinct 
        hour_dim_utc::date as day
        ,user_id
    from data
), distinct_days AS (
    select distinct 
        hour_dim_utc::date as day
    from data
)
select 
    a.day
    ,count(distinct(u.user_id)) as acum_count
from distinct_days as a
join distinct_users_days as u on u.day <= a.day
group by 1 order by 1;

给予:

DAY         ACUM_COUNT
2020-03-10  2
2020-03-11  4
2020-03-12  4
2020-03-14  4
2020-03-15  4
2020-03-16  4
2020-03-17  4
2020-03-18  4
2020-03-19  5

在您的 SQL 中,您可以使用 WHERE DATEDIFF('month', HOUR_DIM.UTC, CURRENT_DATE) BETWEEN 0 AND 0,这样说 WHERE hour_dim.utc &gt;= DATE_TRUNC('month', CURRENT_DATE) 会更具可读性和性能

【讨论】:

【参考方案3】:

如果您每天都有足够的数据,那么 Gordon 的更新答案很好,可以让用户在该月的每一天都有第一天,但​​是当数据像我的示例数据一样稀疏时,您不会得到结果你期待

Gordon 的代码实际上是这样的:

WITH data AS (  
select hour_dim_utc::timestamp_ntz as hour_dim_utc, user_id from values
    ('2020-03-10 9:50', 1 ),
    ('2020-03-10 9:51', 3 ),
    ('2020-03-10 10:51', 3 ),
    ('2020-03-11 9:52', 1 ),
    ('2020-03-11 9:53', 2 ),
    ('2020-03-11 9:54', 0 ),
    ('2020-03-12 9:55', 0 ),
    ('2020-03-12 9:56', 1 ),
    ('2020-03-12 9:57', 3 ),
    ('2020-03-14 9:58', 2 ),
    ('2020-03-15 9:59', 3 ),
    ('2020-03-16 10:00', 2 ),
    ('2020-03-17 10:01', 2 ),
    ('2020-03-18 10:02', 0 ),
    ('2020-03-19 10:04', 11 )
     s( hour_dim_utc, user_id)
)
select 
    first_day
    ,sum(count(*)) over (ORDER BY first_day ASC) as acum 
from (
    select user_id
        ,min(hour_dim_utc::date) as first_day
    from data 
    group by 1
) group by 1;

给出:

FIRST_DAY   ACUM
2020-03-10  2
2020-03-11  4
2020-03-19  5

【讨论】:

以上是关于Snowflake - 在使用窗框或订单时获取不同用户的计数的主要内容,如果未能解决你的问题,请参考以下文章

SnowFlake 算法实现

如何使用 Snowflake Javascript 存储过程或函数遍历表中的所有列?

Snowflake 中是不是有办法允许用户在程序上获取 ddl 但没有使用或所有权权限?

尝试获取不同值时雪花 JSON 未知关键字错误

SQL 按小时获取不同的客户计数

Snowflake /当存储过程作为OWNER执行时如何获取原始调用者