直到当前行的累积唯一值计数
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) 中按时间存储直到零,并与计数一起进行求和