[英]LISTAGG oracle function incorrect results
我想咨詢聚合 function LISTAGG 的這個問題:
select
r.id,
(
select
LISTAGG(
r.id || '##' || cco.id
,
' '
)
from
(
SELECT
co.id
FROM conditions co
START WITH ID = (select cons.id from conditions cons where cons.role_id = r.id)
CONNECT BY PRIOR co.id = co.parent_condition_id
) cco
) conditions_export
from roles r
where r.id in (570, 571, 569)
--r.id between 569 and 571
-- table roles has 2 fields: id (number), name (varchar2)
-- table conditions has 4 fields: id, parent_condition_id, role_id (number), rule (varchar2)
當我在條件r.id in (570, 571, 569)
中使用單個 id(或顯式 ID 列表)時,結果符合預期。
當我不使用 where 子句或使用一些動態范圍時( r.id between 569 and 571
, r.id > 500
, r.id in (select rr.id from roles rr)
),結果包含相同的聚合值對於每一行 - 結果是錯誤的(不符合預期)。
我的數據庫中的示例:
表角色中的值:
id name
--- -----
569 ROLE1
570 ROLE2
571 ROLE3
表條件中的值:
id parent_condition_id role_id rule
------------------------------------------
1657 NULL 569 deny
1659 NULL 570 allow
1667 NULL 571 and
1674 1668 NULL match
1673 1670 NULL allow
1672 1671 NULL allow
1671 1670 NULL and
1670 1669 NULL and
1669 1668 NULL and
1668 1667 NULL and
查詢: ... r.id in (570, 571, 569)
結果:
569 569##1657
570 570##1659
571 571##1667 571##1668 571##1669 571##1670 571##1671 571##1672 571##1673 571##1674
查詢: ... r.id between 569 and 571
結果:
569 569##1657
570 570##1657
571 571##1657
使用此聚合的原因是將當前配置從數據庫導出到文本文件。
問題:你有什么想法,如何解決這個問題?
數據庫版本為 Oracle 19c
您可以使用遞歸子查詢分解而不是分層查詢:
with rcte (role_id, condition_id) as (
select r.id, c.id
from roles r
left join conditions c on c.role_id = r.id
where r.id in (570, 571, 569)
union all
select rcte.role_id, c.id
from rcte
join conditions c on c.parent_condition_id = rcte.condition_id
)
select rcte.role_id,
listagg(rcte.role_id || '##' || rcte.condition_id, ' ')
from rcte
group by rcte.role_id
order by rcte.role_id;
角色編號 | LISTAGG(RCTE.ROLE_ID||'##'||RCTE.CONDITION_ID,'') |
---|---|
569 | 569##1657 |
570 | 570##1659 |
571 | 571##1667 571##1668 571##1674 571##1669 571##1670 571##1673 571##1671 571##1672 |
fiddle包括您的各種初始查詢及其結果。
錨點成員獲取角色和初始條件; 然后遞歸成員查找任何子條件。
第一個問題是結果條件的順序不同
您的原始代碼沒有任何條件排序,因此結果是不確定的。
在您的示例 output 中,它們似乎按條件 ID 排序,因此您可以明確地執行此操作:
select rcte.role_id,
listagg(rcte.role_id || '##' || rcte.condition_id, ' ')
within group (order by rcte.condition_id) as conditions
from rcte
角色編號 | 狀況 |
---|---|
569 | 569##1657 |
570 | 570##1659 |
571 | 571##1667 571##1668 571##1669 571##1670 571##1671 571##1672 571##1673 571##1674 |
正如您提到的級別,您可能希望按該級別排序,盡管它為您的示例提供了不同的結果。 您可以通過跟蹤 CTE 中的虛擬列來獲取級別(我將其稱為lvl
,因為level
是為分層語法保留的):
with rcte (role_id, lvl, condition_id) as (
select r.id, 1, c.id
from roles r
left join conditions c on c.role_id = r.id
where r.id in (570, 571, 569)
union all
select rcte.role_id, rcte.lvl + 1, c.id
from rcte
join conditions c on c.parent_condition_id = rcte.condition_id
)
select rcte.role_id,
listagg(rcte.role_id || '##' || rcte.condition_id, ' ')
within group (order by rcte.lvl) as conditions
from rcte
group by rcte.role_id
order by rcte.role_id;
角色編號 | 狀況 |
---|---|
569 | 569##1657 |
570 | 570##1659 |
571 | 571##1667 571##1668 571##1669 571##1674 571##1670 571##1671 571##1673 571##1672 |
或者您可以按級別排序,然后按條件排序,其中同一級別有多個條件(即多個條件具有相同的父項); 有了這些數據,它沒有什么區別,但如果有多個數據,它會保持確定性:
select rcte.role_id,
listagg(rcte.role_id || '##' || rcte.condition_id, ' ')
within group (order by rcte.lvl, rcte.condition_id) as conditions
from rcte
角色編號 | 狀況 |
---|---|
569 | 569##1657 |
570 | 570##1659 |
571 | 571##1667 571##1668 571##1669 571##1674 571##1670 571##1671 571##1673 571##1672 |
第二個問題是我無法訪問“LEVEL”變量
您問題中的代碼沒有引用它,但如上所示,您可以將其生成為lvl
虛擬列。 目前尚不清楚您將如何使用它 - 如果您希望它出現在 select 列表中,那么您將不得不按它進行分組,這會得到非常不同的結果。 您可以改為包含max(lvl)
; 或者將lvl
連接到條件中(例如571#1#1667
)。 目前還不清楚你想用它做什么。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.