直到当前行的累积唯一值计数

Posted

技术标签:

【中文标题】直到当前行的累积唯一值计数【英文标题】:Cumulative unique value count till current row 【发布时间】:2020-10-01 20:59:23 【问题描述】:

这是我的数据表:

SUBMISSION_DATE SUBMISSION_ID USER_ID USER_NAME 01-MAR-16 8494 20703 安吉拉 01-MAR-16 30173 36396 弗兰克 01-MAR-16 22403 53473 金佰利 01-MAR-16 23965 79722 迈克尔 02-MAR-16 38740 15758 玫瑰 02-MAR-16 34928 20703 安吉拉 02-MAR-16 42769 79722 迈克尔 02-MAR-16 44364 79722 迈克尔 03-MAR-16 45440 20703 安吉拉 16 年 3 月 3 日 49050 36396 弗兰克 16 年 3 月 3 日 50273 79722 迈克尔 04-MAR-16 50344 20703 安吉拉 04-MAR-16 51360 44065 丽莎 04-MAR-16 54404 53473 金佰利 04-MAR-16 61533 79722 迈克尔 05-MAR-16 72852 20703 安吉拉 05-MAR-16 82439 36396 弗兰克 05-MAR-16 9006 36396 弗兰克 16 年 3 月 5 日 74546 38289 帕特里克 05-MAR-16 76487 62529 邦妮 06-MAR-16 90404 20703 安吉拉

现在我的要求是该日期的唯一用户,包括以前的日期。

例子:

2016 年 3 月 1 日st 有 4 个独立用户 Angela、Frank、Kimberly 和 Michael,所以我的独立用户数将是 4。

现在 2nd 2016 年 3 月有 Rose、Angela、Michael 和 Michael,因为我们有 2 个唯一用户 Angela(来了两次)和 Michel(来了三次),所以计数将是 2,包括当前行和之前的所有行只有两个普通用户。

现在到 2016 年 3 月 3 日rd,我们将在当前行和之前的所有行中获得两个普通用户。

2016 年 3 月 4 日th 相同,我们在之前的行中有 2 个共同值。

在 5th 和 6th 我们只有一个是 Angela

所以我的结果将如下所示

SUBMISSION_DATE UNIQUE_USER_COUNT 2016 年 3 月 1 日 4 2016 年 3 月 2 日 2 2016 年 3 月 3 日 2 2016 年 3 月 4 日 2 2016 年 3 月 5 日 1 2016 年 3 月 6 日 1

我试图达到这个结果,但现在确定如何使用 SQL 或特定于 Oracle 以这种方式进行计数,而这不应该通过 PL/SQL 来实现。

【问题讨论】:

从您的描述看来,您需要重复值,而不是唯一值。 如果在 '05-MAR-16' 上没有'Angela',它应该返回什么?那么它应该为 05-MAR-16 和 06-MAR-16 都返回 0 吗? 如果某个日期(例如 '02-mar-2016')缺少行,它应该返回什么?是否应该为所有日期返回 0>02-mar-2016? 【参考方案1】:

使用窗口函数dense_rank()rank() 获取每个用户每天的出现次数,然后使用条件聚合获取group by submission_date

select t.submission_date,
       count(case when t.rnk = t.day then 1 end) unique_user_count
from (
  select t.submission_date,
    dense_rank() over (order by t.submission_date) day,
    rank() over (partition by t.user_id order by t.submission_date) rnk
  from (select distinct submission_date, user_id from tablename) t
) t
group by t.submission_date
order by t.submission_date

请参阅demo。 结果:

> SUBMISSION_DATE | UNIQUE_USER_COUNT
> :-------------- | ----------------:
> 01-MAR-16       |                 4
> 02-MAR-16       |                 2
> 03-MAR-16       |                 2
> 04-MAR-16       |                 2
> 05-MAR-16       |                 1
> 06-MAR-16       |                 1

【讨论】:

谢谢这有意义让我试试 @AtulSinghRathore 如果此答案解决了您的问题,请考虑通过单击复选标记接受它。【参考方案2】:

对于每个用户(假设由user_id 标识)计算他出现的日期数。对于任何特定日期,到目前为止出现在所有日期的用户的累积总和等于该日期的顺序:

with t (submission_date, user_id) as (
select date '2016-03-01', 20703 from dual union all
select date '2016-03-01', 36396 from dual union all
select date '2016-03-01', 53473 from dual union all
select date '2016-03-01', 79722 from dual union all
select date '2016-03-02', 15758 from dual union all
select date '2016-03-02', 20703 from dual union all
select date '2016-03-02', 79722 from dual union all
select date '2016-03-02', 79722 from dual union all
select date '2016-03-03', 20703 from dual union all
select date '2016-03-03', 36396 from dual union all
select date '2016-03-03', 79722 from dual union all
select date '2016-03-04', 20703 from dual union all
select date '2016-03-04', 44065 from dual union all
select date '2016-03-04', 53473 from dual union all
select date '2016-03-04', 79722 from dual union all
select date '2016-03-05', 20703 from dual union all
select date '2016-03-05', 36396 from dual union all
select date '2016-03-05', 36396 from dual union all
select date '2016-03-05', 38289 from dual union all
select date '2016-03-05', 62529 from dual union all
select date '2016-03-06', 20703 from dual
), d (submission_date, user_id) as (
  select distinct submission_date, user_id
  from t
), preprocess (submission_date, user_id, rn, dr) as (
  select submission_date
       , user_id
       , row_number() over (partition by user_id order by submission_date)
       , dense_rank() over (order by submission_date)
  from d order by 1,2
)
select submission_date, count(*)
from preprocess
where dr = rn
group by submission_date
order by submission_date

Db fiddle here.

如果存在等效于multiset intersect 的窗口函数,则此示例几乎可以通过一条线解决:-)。

【讨论】:

【参考方案3】:

只是为了好玩,另一种基于递归子查询分解和多集运算符的解决方案(不用于生产,因为性能慢):

with tablename(SUBMISSION_DATE , SUBMISSION_ID, USER_ID, USER_NAME) as (
select to_date('01-Mar-2016','dd-mon-yyyy'), 8494, 20703, 'Angela' from dual union all
select to_date('01-Mar-2016','dd-mon-yyyy'), 30173, 36396, 'Frank' from dual union all
select to_date('01-Mar-2016','dd-mon-yyyy'), 22403, 53473, 'Kimberly' from dual union all
select to_date('01-Mar-2016','dd-mon-yyyy'), 23965, 79722, 'Michael' from dual union all
select to_date('02-Mar-2016','dd-mon-yyyy'), 38740, 15758, 'Rose' from dual union all
select to_date('02-Mar-2016','dd-mon-yyyy'), 34928, 20703, 'Angela' from dual union all
select to_date('02-Mar-2016','dd-mon-yyyy'), 42769, 79722, 'Michael' from dual union all
select to_date('02-Mar-2016','dd-mon-yyyy'), 44364, 79722, 'Michael' from dual union all
select to_date('03-Mar-2016','dd-mon-yyyy'), 45440, 20703, 'Angela' from dual union all
select to_date('03-Mar-2016','dd-mon-yyyy'), 49050, 36396, 'Frank' from dual union all
select to_date('03-Mar-2016','dd-mon-yyyy'), 50273, 79722, 'Michael' from dual union all
select to_date('04-Mar-2016','dd-mon-yyyy'), 50344, 20703, 'Angela' from dual union all
select to_date('04-Mar-2016','dd-mon-yyyy'), 51360, 44065, 'Lisa' from dual union all
select to_date('04-Mar-2016','dd-mon-yyyy'), 54404, 53473, 'Kimberly' from dual union all
select to_date('04-Mar-2016','dd-mon-yyyy'), 61533, 79722, 'Michael' from dual union all
select to_date('05-Mar-2016','dd-mon-yyyy'), 72852, 20703, 'Angela' from dual union all
select to_date('05-Mar-2016','dd-mon-yyyy'), 82439, 36396, 'Frank' from dual union all
select to_date('05-Mar-2016','dd-mon-yyyy'), 9006, 36396, 'Frank' from dual union all
select to_date('05-Mar-2016','dd-mon-yyyy'), 74546, 38289, 'Patrick' from dual union all
select to_date('05-Mar-2016','dd-mon-yyyy'), 76487, 62529, 'Bonnie' from dual union all
select to_date('06-Mar-2016','dd-mon-yyyy'), 90404, 20703, 'Angela' from dual 
)
,agg as (
    select SUBMISSION_DATE
        , cast(collect(distinct USER_NAME) as sys.ku$_vcnt) users
        , row_number()over(order by SUBMISSION_DATE) n
    from tablename
    group by SUBMISSION_DATE
)
,cte(sdate,users) as (
     select SUBMISSION_DATE
        , users
     from agg
     where n=1
     union all
     select SUBMISSION_DATE
        , cte.users multiset intersect agg.users users
     from cte, agg
     where sdate + 1 = SUBMISSION_DATE
)
select
   sdate,
   cardinality(users) cnt,
   users
from cte;

结果:

SDATE                      CNT USERS
------------------- ---------- ----------------------------------------------------------------------------------------------------
2016-03-01 00:00:00          4 KU$_VCNT('Angela', 'Frank', 'Kimberly', 'Michael')
2016-03-02 00:00:00          2 KU$_VCNT('Angela', 'Michael')
2016-03-03 00:00:00          2 KU$_VCNT('Angela', 'Michael')
2016-03-04 00:00:00          2 KU$_VCNT('Angela', 'Michael')
2016-03-05 00:00:00          1 KU$_VCNT('Angela')
2016-03-06 00:00:00          1 KU$_VCNT('Angela')

【讨论】:

支持,因为这是我一开始解决问题的方式。我想附加 dbfiddle,但由于 cast(collect ... as sys.ku$_vcnt) 无法在 dbfiddle.uk 上工作而失败(网站失败、空结果或 ORA-00932: inconsistent datatypes: expected - got ANYDATA)。我相信您的示例适用于您的数据库,但不知道为什么它不适用于 dbfiddle。 @TomášZáluský DBFIDDLE 不支持集合类型 :) 还有 VARRAYs 这是不可行的,因为我提供的数据是一个样本,并且可能会有所不同,它不固定

以上是关于直到当前行的累积唯一值计数的主要内容,如果未能解决你的问题,请参考以下文章

如何获得解码/未透视行的唯一计数(列到行)

分组值在 SQL (maria DB) 中按时间存储直到零,并与计数一起进行求和

用于计算唯一值的简单数据透视表

DB2 SQL 比 SUM+CASE 更好的方法来获取基于 ID 的唯一值计数

POWER BI 如何计算不唯一选项的累积占比

如何获取仅分配给一个值而没有其他值的唯一 ID 的计数?