选择列表列/表达式(包括短路的 case 表达式)的最终评估发生在检索数据之后。到那时,任何分组等都已经完成。
这种影响不仅发生在listagg()
,它可以在返回表达式中的任何聚合或分析函数调用中看到 - 尽管除非有副作用,否则很难发现。
作为一个演示,我创建了一个简单的包,它有一个可以从查询中调用的函数:
create package p as
n number := 0;
function f return number;
end;
/
create package body p as
function f return number as
begin
n := n + 1;
return n;
end;
end;
/
这本质上是在模拟特定于会话的序列;序列也展示了这种行为,但显然是出于不同的原因,所以我不想为此使用一个。
在 case 表达式中调用该函数可以达到您的预期;它仅在条件匹配时调用:
select dep,
case
when dep = 'BAR' then
p.f
else
-1
end as id_list
from emp;
DEP ID_LIST
---------- -------
FOO -1
...
BAR 1
BAR 2
BAR 3
FOO -1
select p.f from dual;
F
----------
4
仅当条件匹配时才调用该函数。其执行计划仅显示全表扫描:
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 13 | 91 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| EMP | 13 | 91 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
改为使用聚合调用:
select dep,
case
when dep = 'BAR' then
count(p.f)
else
-1
end as id_list
from emp
group by dep;
DEP ID_LIST
---------- -------
FOO -1
BAR 3
select p.f from dual;
F
----------
18
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 13 | 91 | 4 (25)| 00:00:01 |
| 1 | HASH GROUP BY | | 13 | 91 | 4 (25)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 13 | 91 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
...该函数被调用了 13 次而不是 3 次;该计划逐步显示哈希组,这必须在评估案例之前发生在所有检索到的行中。
同样对于分析版本:
select dep,
case
when dep = 'BAR' then
count(p.f) over (partition by dep)
else
-1
end as id_list
from emp;
DEP ID_LIST
---------- -------
BAR 3
BAR 3
BAR 3
FOO -1
...
select p.f from dual;
F
----------
32
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 13 | 91 | 4 (25)| 00:00:01 |
| 1 | WINDOW SORT | | 13 | 91 | 4 (25)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 13 | 91 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
...再次调用该函数 13 次,因为窗口排序(因此分析计算)是在可以评估案例表达式之前完成的。
所以问题不在于返回表达式(listagg()
在你的情况下)在不应该的情况下在 case 表达式中被评估;在考虑 case 表达式条件之前,它正在被评估并抛出异常。