Oracle-RETURNING与聚合函数结合

时间:2018-09-14 19:07:21

标签: sql oracle aggregate-functions sql-returning

Oracle支持RETURNING子句,该子句可能非常有用。

例如数据:

CREATE TABLE t(Id INT, Val varchar2(50));

INSERT INTO t(Id, Val)
SELECT 10,'a' FROM dual
UNION ALL SELECT 20,'b' FROM dual
UNION ALL SELECT 30,'a' FROM dual
UNION ALL SELECT 40,'b' FROM dual;

查询:

DECLARE
   l_cnt INT;
BEGIN
   DELETE FROM t RETURNING COUNT(*) INTO l_cnt;
   DBMS_OUTPUT.put_line('l_cnt: ' || l_cnt);
END;
  

l_cnt:4

它支持MIN / MAX / AVG / SUM / LISTAGG:

DECLARE
   l_max INT;
   l_min INT;
   l_str VARCHAR2(100);
BEGIN
   DELETE FROM t 
   RETURNING MAX(id), MIN(id), LISTAGG(id, ',') WITHIN GROUP(ORDER BY id) 
   INTO l_max, l_min, l_str;
   DBMS_OUTPUT.put_line('l_max:'||l_max||' l_min:'||l_min||' l_str:'|| l_str);
END;
  

l_max:40 l_min:10 l_str:10,20,30,40

不幸的是,当与DISTINCT关键字结合使用时,出现错误:

DECLARE
   l_distinct_cnt INT;
BEGIN
   DELETE FROM t 
   RETURNING COUNT(DISTINCT val) INTO l_distinct_cnt ;
   DBMS_OUTPUT.put_line('l_distinct_cnt:' || l_distinct_cnt );
END;
  

ORA-00934:此处不允许使用分组功能

db<>fiddle demo

问题是为什么不允许使用带有DISTINCT的聚合函数? 我正在寻找官方消息的答案。


编辑:

请注意,COUNT(DISTINCT ...)仅是示例。 SUM(col)/SUM(DISTINCT col)和支持DISTINCT关键字的任何聚合函数的行为相同。

SUM(val) vs SUM(DISTINCT val)

2 个答案:

答案 0 :(得分:3)

首先,文档和实际功能有点不同步,因此“官方资源”将不会透露细节。

10g R2(https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/returninginto_clause.htm)的语法图如下 enter image description here

在11g(https://docs.oracle.com/cd/E11882_01/appdev.112/e25519/returninginto_clause.htm)中,这分为两个部分:static_returning_clause(用于插入,更新,删除)和dynamic_returning_clause(用于立即执行)。我们对DML感兴趣。 enter image description here

因此对于10g,有一个单行表达式,根据文档,该表达式为返回表中单行的表达式。执行该语句后可以得出DML语句必须影响单行还是单行这是一个微妙的问题(例如,使用聚合函数)。我假设该想法是在DML操作影响单行时使用此语法(与bulk collect into相对);不使用会返回受影响行的单行的聚合函数。

因此,不清楚返回in子句中的聚合函数。而且,对于11g,仅在返回关键字后可能会出现一个列名,因此即使实际上像abs(column_name)之类的表达式也不能不提及gregation_function(column_name),尽管实际上它可以工作。

因此,严格来说,没有记录具有聚合功能的功能,特别是对于11g,12c,18c,您不能依赖它。

相反,您可以使用“大量收集到”(和set运算符来获取不同的元素集)

SQL> create type str_tab as table of varchar2(4000)
  2  /

Type created.

SQL> set serveroutput on
SQL> declare
  2    i int;
  3    a str_tab;
  4  begin
  5    delete from t returning val bulk collect into a;
  6    dbms_output.put_line('cnt all ' || a.count || ' cnt distinct ' || set(a).count);
  7    rollback;
  8  end;
  9  /
cnt all 4 cnt distinct 2

PL/SQL procedure successfully completed.

还要注意错误消息。它清楚地说

  

ORA-00934:此处不允许使用功能

不仅如此例中的“不允许有区别”

SQL> select listagg(distinct val) within group (order by val) str from t;
select listagg(distinct val) within group (order by val) str from t
       *
ERROR at line 1:
ORA-30482: DISTINCT option not allowed for this function

答案 1 :(得分:0)

主要原因是SQL是不可组合的。 C. J. Date至少在我在北德克萨斯州参加的2009届课堂上表明,SQL是不可组合的。因为它是不可组合的,所以某些东西不是免费的。所谓免费,是指我什至没有思想。但是Server Technologies的人员非常聪明,我相信管理“添加返回功能”项目的人员有意识地确定了分界线。他们显然决定不“完全”编写解析器。我怀疑这是因为他们知道,如果他们采取100%可支持性的立场,那么一旦SQL的其他部分得到增强,那么他们也必须花时间来增强语言的其他部分。

我非常欣赏ST的SQL解析器执行速度以及它产生错误结果的罕见程度。但是我想知道,但占主导地位的查询语言至少是可组合的,世界会比现在更好吗?