Oracle SQL - 如何比较同一 table 中两个集合之间的行数?
Oracle SQL - How to compare the number of rows between 2 sets in the same table?
在 table security_privileges
我想比较最后 version_number
的行数与 version_number
之前 last_update
的行数= 01.03.2020.
我突出显示了结果应识别的两组行。第一组有8排,第二组有6排。
我期望从这个例子中得到的输出就在底部。
下面我尝试了这个sequel,但它并没有真正起作用:
SELECT
cur.user_id,
cur.version_number,
cur.last_update,
(cur.last_update- prv.last_update)
FROM
security_privileges prv
INNER JOIN security_privileges cur
ON cur.version_number = prv.version_number +2
[![输出][3]][3]
您可以使用 window 函数和条件聚合。内部查询计算指定日期之前和总体的最大版本号:
select user_id, max_vn, max_vn_pre,
sum(case when version_number = max_vn then 1 else 0 end) as max_vn_cnt,
sum(case when version_number = max_vn_pre then 1 else 0 end) as max_vn_pre_cnt
from (select sp.*,
max(version_number) over (partition by user_id) as max_vn,
max(case when last_update < date '2020-03-01' then version_number end) over (partition by user_id) as max_vn_pre
from security_privileges sp
) sp
group by user_id, max_vn, max_vn_pre;
这假设您想要每个 user_id 的结果。如果不是,只需删除两个 windowing 子句中的 partition by
。
我会使用一些不同的方法,但仍然使用 window 分析函数:
- 起初,我只过滤了你想要的行,使用
dense_rank
和 partition by user_id, case when last_update>=date'2020-03-01' then 1 else 2 end
,所以这个 dense_rank 将 return 1 用于所有需要的行,我们可以轻松过滤然后使用 drnk=1
:
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=b6925860bfa16d2f222d428f508c1b50
select *
from
(
SELECT
prv.*
,dense_rank()
over(
partition by
user_id,
case when last_update>=date'2020-03-01' then 1 else 2 end
order by version_number desc
) as drnk
FROM
security_privileges prv
) v
where v.drnk = 1;
结果:
USER_ID SECURITY_PRIVILEGE_ID VERSION_NUMBER LAST_USER_UPDATE_ID LAST_UPDATE DRNK
---------- --------------------- -------------- ------------------- ------------------- ----------
9867 20011 16 9954 2020-08-31 00:00:00 1
9867 20059 16 9955 2020-08-31 00:00:00 1
9867 20003 16 9956 2020-08-31 00:00:00 1
9867 20069 16 9957 2020-08-31 00:00:00 1
9867 20004 16 9958 2020-08-31 00:00:00 1
9867 20046 16 9959 2020-08-31 00:00:00 1
9867 20003 14 9832 2017-06-28 00:00:00 1
9867 20059 14 9833 2017-06-28 00:00:00 1
9867 20046 14 9834 2017-06-28 00:00:00 1
9867 20004 14 9835 2017-06-28 00:00:00 1
9867 20045 14 9836 2017-06-28 00:00:00 1
9867 20002 14 9837 2017-06-28 00:00:00 1
9867 20011 14 9838 2017-06-28 00:00:00 1
9867 20069 14 9839 2017-06-28 00:00:00 1
14 rows selected.
- 第二步更简单:我们只需要聚合这些行:
使用条件聚合:https://dbfiddle.uk/?rdbms=oracle_18&fiddle=6467247bfde1dd978da4ce51067e3d70
select
user_id,
min(version_number) version_number1,
max(version_number) version_number2,
count(decode(grp,1,0)) cnt1,
count(decode(grp,2,0)) cnt2,
listagg(decode(grp,1,security_privilege_id), ',')
within group(order by security_privilege_id) sec_priv_ids1,
listagg(decode(grp,2,security_privilege_id), ',')
within group(order by security_privilege_id) sec_priv_ids2
from
(
SELECT
prv.*
,case when last_update>=date'2020-03-01' then 1 else 2 end grp
,dense_rank()
over(
partition by
user_id,
case when last_update>=date'2020-03-01' then 1 else 2 end
order by version_number desc
) as drnk
FROM
security_privileges prv
) v
where v.drnk = 1
group by user_id;
结果:
USER_ID VERSION_NUMBER1 VERSION_NUMBER2 CNT1 CNT2 SEC_PRIV_IDS1 SEC_PRIV_IDS2
---------- --------------- --------------- ---------- ---------- ---------------------------------------- --------------------------------------------------
9867 14 16 6 8 20003,20004,20011,20046,20059,20069 20002,20003,20004,20011,20045,20046,20059,20069
或标准聚合 如您最后的屏幕截图所示:https://dbfiddle.uk/?rdbms=oracle_18&fiddle=1eb736011e32754dc444c120946e8bea
select
user_id,
grp,
version_number,
last_update,
count(*) cnt,
listagg(security_privilege_id, ',')
within group(order by security_privilege_id) sec_priv_ids
from
(
SELECT
prv.*
,case when last_update>=date'2020-03-01' then 1 else 2 end grp
,dense_rank()
over(
partition by
user_id,
case when last_update>=date'2020-03-01' then 1 else 2 end
order by version_number desc
) as drnk
FROM
security_privileges prv
) v
where v.drnk = 1
group by user_id,grp,version_number,last_update
order by 1,2;
结果:
USER_ID GRP VERSION_NUMBER LAST_UPDATE CNT SEC_PRIV_IDS
---------- ---------- -------------- ------------------- ---------- --------------------------------------------------
9867 1 16 2020-08-31 00:00:00 6 20003,20004,20011,20046,20059,20069
9867 2 14 2017-06-28 00:00:00 8 20002,20003,20004,20011,20045,20046,20059,20069
CTE 中包含样本数据的完整测试用例:
with security_privileges(user_id, security_privilege_id, version_number, last_user_update_id, last_update) as (
select 9867, 20011, 16, 9954, date'2020-08-31' from dual union all
select 9867, 20059, 16, 9955, date'2020-08-31' from dual union all
select 9867, 20003, 16, 9956, date'2020-08-31' from dual union all
select 9867, 20069, 16, 9957, date'2020-08-31' from dual union all
select 9867, 20004, 16, 9958, date'2020-08-31' from dual union all
select 9867, 20046, 16, 9959, date'2020-08-31' from dual union all
select 9867, 20011, 15, 9960, date'2020-08-13' from dual union all
select 9867, 20059, 15, 9961, date'2020-08-13' from dual union all
select 9867, 20004, 15, 9962, date'2020-08-13' from dual union all
select 9867, 20003, 15, 9963, date'2020-08-13' from dual union all
select 9867, 20046, 15, 9964, date'2020-08-13' from dual union all
select 9867, 20003, 14, 9832, date'2017-06-28' from dual union all
select 9867, 20059, 14, 9833, date'2017-06-28' from dual union all
select 9867, 20046, 14, 9834, date'2017-06-28' from dual union all
select 9867, 20004, 14, 9835, date'2017-06-28' from dual union all
select 9867, 20045, 14, 9836, date'2017-06-28' from dual union all
select 9867, 20002, 14, 9837, date'2017-06-28' from dual union all
select 9867, 20011, 14, 9838, date'2017-06-28' from dual union all
select 9867, 20069, 14, 9839, date'2017-06-28' from dual union all
select 9867, 20059, 13, 9840, date'2017-06-21' from dual union all
select 9867, 20011, 13, 9841, date'2017-06-21' from dual union all
select 9867, 20045, 13, 9842, date'2017-06-21' from dual union all
select 9867, 20003, 13, 9843, date'2017-06-21' from dual union all
select 9867, 20046, 13, 9844, date'2017-06-21' from dual union all
select 9867, 20002, 13, 9845, date'2017-06-21' from dual union all
select 9867, 20069, 13, 9846, date'2017-06-21' from dual union all
select 9867, 20069, 12, 9127, date'2017-06-02' from dual union all
select 9867, 20046, 12, 9128, date'2017-06-02' from dual union all
select 9867, 20003, 12, 9127, date'2017-06-02' from dual union all
select 9867, 20059, 12, 9128, date'2017-06-02' from dual union all
select 9867, 20011, 12, 9128, date'2017-06-02' from dual
)
select
user_id,
grp,
version_number,
last_update,
count(*) cnt,
listagg(security_privilege_id, ',')
within group(order by security_privilege_id) sec_priv_ids
from
(
SELECT
prv.*
,case when last_update>=date'2020-03-01' then 1 else 2 end grp
,dense_rank()
over(
partition by
user_id,
case when last_update>=date'2020-03-01' then 1 else 2 end
order by version_number desc
) as drnk
FROM
security_privileges prv
) v
where v.drnk = 1
group by user_id,grp,version_number,last_update
order by 1,2;
在 table security_privileges
我想比较最后 version_number
的行数与 version_number
之前 last_update
的行数= 01.03.2020.
我突出显示了结果应识别的两组行。第一组有8排,第二组有6排。
我期望从这个例子中得到的输出就在底部。
下面我尝试了这个sequel,但它并没有真正起作用:
SELECT
cur.user_id,
cur.version_number,
cur.last_update,
(cur.last_update- prv.last_update)
FROM
security_privileges prv
INNER JOIN security_privileges cur
ON cur.version_number = prv.version_number +2
[![输出][3]][3]
您可以使用 window 函数和条件聚合。内部查询计算指定日期之前和总体的最大版本号:
select user_id, max_vn, max_vn_pre,
sum(case when version_number = max_vn then 1 else 0 end) as max_vn_cnt,
sum(case when version_number = max_vn_pre then 1 else 0 end) as max_vn_pre_cnt
from (select sp.*,
max(version_number) over (partition by user_id) as max_vn,
max(case when last_update < date '2020-03-01' then version_number end) over (partition by user_id) as max_vn_pre
from security_privileges sp
) sp
group by user_id, max_vn, max_vn_pre;
这假设您想要每个 user_id 的结果。如果不是,只需删除两个 windowing 子句中的 partition by
。
我会使用一些不同的方法,但仍然使用 window 分析函数:
- 起初,我只过滤了你想要的行,使用
dense_rank
和partition by user_id, case when last_update>=date'2020-03-01' then 1 else 2 end
,所以这个 dense_rank 将 return 1 用于所有需要的行,我们可以轻松过滤然后使用drnk=1
:
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=b6925860bfa16d2f222d428f508c1b50
select *
from
(
SELECT
prv.*
,dense_rank()
over(
partition by
user_id,
case when last_update>=date'2020-03-01' then 1 else 2 end
order by version_number desc
) as drnk
FROM
security_privileges prv
) v
where v.drnk = 1;
结果:
USER_ID SECURITY_PRIVILEGE_ID VERSION_NUMBER LAST_USER_UPDATE_ID LAST_UPDATE DRNK
---------- --------------------- -------------- ------------------- ------------------- ----------
9867 20011 16 9954 2020-08-31 00:00:00 1
9867 20059 16 9955 2020-08-31 00:00:00 1
9867 20003 16 9956 2020-08-31 00:00:00 1
9867 20069 16 9957 2020-08-31 00:00:00 1
9867 20004 16 9958 2020-08-31 00:00:00 1
9867 20046 16 9959 2020-08-31 00:00:00 1
9867 20003 14 9832 2017-06-28 00:00:00 1
9867 20059 14 9833 2017-06-28 00:00:00 1
9867 20046 14 9834 2017-06-28 00:00:00 1
9867 20004 14 9835 2017-06-28 00:00:00 1
9867 20045 14 9836 2017-06-28 00:00:00 1
9867 20002 14 9837 2017-06-28 00:00:00 1
9867 20011 14 9838 2017-06-28 00:00:00 1
9867 20069 14 9839 2017-06-28 00:00:00 1
14 rows selected.
- 第二步更简单:我们只需要聚合这些行: 使用条件聚合:https://dbfiddle.uk/?rdbms=oracle_18&fiddle=6467247bfde1dd978da4ce51067e3d70
select
user_id,
min(version_number) version_number1,
max(version_number) version_number2,
count(decode(grp,1,0)) cnt1,
count(decode(grp,2,0)) cnt2,
listagg(decode(grp,1,security_privilege_id), ',')
within group(order by security_privilege_id) sec_priv_ids1,
listagg(decode(grp,2,security_privilege_id), ',')
within group(order by security_privilege_id) sec_priv_ids2
from
(
SELECT
prv.*
,case when last_update>=date'2020-03-01' then 1 else 2 end grp
,dense_rank()
over(
partition by
user_id,
case when last_update>=date'2020-03-01' then 1 else 2 end
order by version_number desc
) as drnk
FROM
security_privileges prv
) v
where v.drnk = 1
group by user_id;
结果:
USER_ID VERSION_NUMBER1 VERSION_NUMBER2 CNT1 CNT2 SEC_PRIV_IDS1 SEC_PRIV_IDS2
---------- --------------- --------------- ---------- ---------- ---------------------------------------- --------------------------------------------------
9867 14 16 6 8 20003,20004,20011,20046,20059,20069 20002,20003,20004,20011,20045,20046,20059,20069
或标准聚合 如您最后的屏幕截图所示:https://dbfiddle.uk/?rdbms=oracle_18&fiddle=1eb736011e32754dc444c120946e8bea
select
user_id,
grp,
version_number,
last_update,
count(*) cnt,
listagg(security_privilege_id, ',')
within group(order by security_privilege_id) sec_priv_ids
from
(
SELECT
prv.*
,case when last_update>=date'2020-03-01' then 1 else 2 end grp
,dense_rank()
over(
partition by
user_id,
case when last_update>=date'2020-03-01' then 1 else 2 end
order by version_number desc
) as drnk
FROM
security_privileges prv
) v
where v.drnk = 1
group by user_id,grp,version_number,last_update
order by 1,2;
结果:
USER_ID GRP VERSION_NUMBER LAST_UPDATE CNT SEC_PRIV_IDS
---------- ---------- -------------- ------------------- ---------- --------------------------------------------------
9867 1 16 2020-08-31 00:00:00 6 20003,20004,20011,20046,20059,20069
9867 2 14 2017-06-28 00:00:00 8 20002,20003,20004,20011,20045,20046,20059,20069
CTE 中包含样本数据的完整测试用例:
with security_privileges(user_id, security_privilege_id, version_number, last_user_update_id, last_update) as (
select 9867, 20011, 16, 9954, date'2020-08-31' from dual union all
select 9867, 20059, 16, 9955, date'2020-08-31' from dual union all
select 9867, 20003, 16, 9956, date'2020-08-31' from dual union all
select 9867, 20069, 16, 9957, date'2020-08-31' from dual union all
select 9867, 20004, 16, 9958, date'2020-08-31' from dual union all
select 9867, 20046, 16, 9959, date'2020-08-31' from dual union all
select 9867, 20011, 15, 9960, date'2020-08-13' from dual union all
select 9867, 20059, 15, 9961, date'2020-08-13' from dual union all
select 9867, 20004, 15, 9962, date'2020-08-13' from dual union all
select 9867, 20003, 15, 9963, date'2020-08-13' from dual union all
select 9867, 20046, 15, 9964, date'2020-08-13' from dual union all
select 9867, 20003, 14, 9832, date'2017-06-28' from dual union all
select 9867, 20059, 14, 9833, date'2017-06-28' from dual union all
select 9867, 20046, 14, 9834, date'2017-06-28' from dual union all
select 9867, 20004, 14, 9835, date'2017-06-28' from dual union all
select 9867, 20045, 14, 9836, date'2017-06-28' from dual union all
select 9867, 20002, 14, 9837, date'2017-06-28' from dual union all
select 9867, 20011, 14, 9838, date'2017-06-28' from dual union all
select 9867, 20069, 14, 9839, date'2017-06-28' from dual union all
select 9867, 20059, 13, 9840, date'2017-06-21' from dual union all
select 9867, 20011, 13, 9841, date'2017-06-21' from dual union all
select 9867, 20045, 13, 9842, date'2017-06-21' from dual union all
select 9867, 20003, 13, 9843, date'2017-06-21' from dual union all
select 9867, 20046, 13, 9844, date'2017-06-21' from dual union all
select 9867, 20002, 13, 9845, date'2017-06-21' from dual union all
select 9867, 20069, 13, 9846, date'2017-06-21' from dual union all
select 9867, 20069, 12, 9127, date'2017-06-02' from dual union all
select 9867, 20046, 12, 9128, date'2017-06-02' from dual union all
select 9867, 20003, 12, 9127, date'2017-06-02' from dual union all
select 9867, 20059, 12, 9128, date'2017-06-02' from dual union all
select 9867, 20011, 12, 9128, date'2017-06-02' from dual
)
select
user_id,
grp,
version_number,
last_update,
count(*) cnt,
listagg(security_privilege_id, ',')
within group(order by security_privilege_id) sec_priv_ids
from
(
SELECT
prv.*
,case when last_update>=date'2020-03-01' then 1 else 2 end grp
,dense_rank()
over(
partition by
user_id,
case when last_update>=date'2020-03-01' then 1 else 2 end
order by version_number desc
) as drnk
FROM
security_privileges prv
) v
where v.drnk = 1
group by user_id,grp,version_number,last_update
order by 1,2;