ITPub博客

首页 > 数据库 > Oracle > [20241227]标量子查询返回多值问题.txt

[20241227]标量子查询返回多值问题.txt

原创 Oracle 作者:lfree 时间:2024-12-28 21:17:19 0 删除 编辑

[20241227]标量子查询返回多值问题.txt

--//一看标题,以为写错了,实际上标量子查询不可能返回多值的,有时候需要返回多个值,一般我的建议是拼接在一起输出。
--//通过例子说明:

1.环境:
SCOTT@book01p> @ ver2
==============================
PORT_STRING                   : x86_64/Linux 2.4.xx
VERSION                       : 21.0.0.0.0
BANNER                        : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
BANNER_FULL                   : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
BANNER_LEGACY                 : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
CON_ID                        : 0
PL/SQL procedure successfully completed.

2.例子:
SCOTT@book01p> select empno,ename,sal,(select dname,loc from dept where deptno=emp.deptno) dname from emp where sal>=3000;
select empno,ename,sal,(select dname,loc from dept where deptno=emp.deptno) dname from emp where sal>=3000
                        *
ERROR at line 1:
ORA-00913: too many values

--//标量子查询不可能返回多值.在实际的生产系统中,可以发现开发这样写:

SCOTT@book01p> select empno,ename,sal,(select dname from dept where deptno=emp.deptno) dname ,(select loc from dept where deptno=emp.deptno) loc from emp where sal>=3000;
     EMPNO ENAME             SAL DNAME                          LOC
---------- ---------- ---------- ------------------------------ -------------
      7788 SCOTT            3000 RESEARCH                       DALLAS
      7839 KING             5000 ACCOUNTING                     NEW YORK
      7902 FORD             3000 RESEARCH                       DALLAS
--//当然我的例子很简单,一般这样的情况采用连接方式,实际上生产系统的查询要很复杂,这样写明显扫描dept表。

Plan hash value: 3707356765
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | E-Rows |E-Bytes| Cost (%CPU)| E-Time   |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |        |       |    12 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| DEPT    |      1 |    13 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | PK_DEPT |      1 |       |     0   (0)|          |
|   3 |  TABLE ACCESS BY INDEX ROWID| DEPT    |      1 |    11 |     1   (0)| 00:00:01 |
|*  4 |   INDEX UNIQUE SCAN         | PK_DEPT |      1 |       |     0   (0)|          |
|*  5 |  TABLE ACCESS FULL          | EMP     |      7 |   119 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$2 / "DEPT"@"SEL$2"
   2 - SEL$2 / "DEPT"@"SEL$2"
   3 - SEL$3 / "DEPT"@"SEL$3"
   4 - SEL$3 / "DEPT"@"SEL$3"
   5 - SEL$1 / "EMP"@"SEL$1"
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("DEPTNO"=:B1)
   4 - access("DEPTNO"=:B1)
   5 - filter("SAL">=3000)

--//一般我的建议多少情况下为了显示,可以拼接在一起输出。
SCOTT@book01p> select empno,ename,sal,(select rpad(dname,13,' ')||loc from dept where deptno=emp.deptno) dname_loc from emp where sal>=3000;
     EMPNO ENAME             SAL DNAME_LOC
---------- ---------- ---------- ---------------------------------------
      7788 SCOTT            3000 RESEARCH     DALLAS
      7839 KING             5000 ACCOUNTING   NEW YORK
      7902 FORD             3000 RESEARCH     DALLAS

3.也可以定义1个type变成单值输出。

SCOTT@book01p> CREATE OR REPLACE TYPE t_dept AS OBJECT ( b_DNAME  VARCHAR2(14), b_loc  VARCHAR2(13) )
  2  /
Type created.

SCOTT@book01p> select empno,ename,sal,(select t_dept(dept.dname,dept.loc) from dept where deptno=emp.deptno) c40 from emp where sal>=3000;
     EMPNO ENAME             SAL C40(B_DNAME, B_LOC)
---------- ---------- ---------- ----------------------------------------
      7788 SCOTT            3000 T_DEPT('RESEARCH', 'DALLAS')
      7839 KING             5000 T_DEPT('ACCOUNTING', 'NEW YORK')
      7902 FORD             3000 T_DEPT('RESEARCH', 'DALLAS')

--//这样并没有满足真实生产系统的需求,改写如下:

SCOTT@book01p> select x.empno,x.ename,x.sal,x.c40.b_dname,x.c40.b_loc from (select empno,ename,sal,(select t_dept(dept.dname,dept.loc) from dept where deptno=emp.deptno) c40 from emp where sal>=3000) x;

     EMPNO ENAME             SAL C40.B_DNAME    C40.B_LOC
---------- ---------- ---------- -------------- -------------
      7788 SCOTT            3000 RESEARCH       DALLAS
      7839 KING             5000 ACCOUNTING     NEW YORK
      7902 FORD             3000 RESEARCH       DALLAS
COTT@book01p> @hash

HASH_VALUE SQL_ID        CHILD_NUMBER KGL_BUCKET PLAN_HASH_VALUE HASH_HEX   SQL_EXEC_START      SQL_EXEC_ID
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
1884089577 8pz1ugts4tv79            0      60649      2981343222  704cece9  2024-12-28 10:11:44    16777218

SCOTT@book01p> @ expand_sql_text 8pz1ugts4tv79
SELECT "A1"."EMPNO" "EMPNO","A1"."ENAME" "ENAME","A1"."SAL" "SAL",("A1"."C40")."B_DNAME" "C40.B_DNAME",("A1"."C40")."B_LOC" "C40.B_LOC" FROM  (SELECT "A2"."EMPNO" "EMPNO","A2"."ENAME" "ENAME","A2"."SAL" "SAL", (SELECT "T_DEPT"("A3"."DNAME","A3"."LOC")
"T_DEPT(DEPT.DNAME,DEPT.LOC)" FROM "SCOTT"."DEPT" "A3" WHERE "A3"."DEPTNO"="A2"."DEPTNO") "C40" FROM "SCOTT"."EMP" "A2" WHERE "A2"."SAL">=3000) "A1"

PL/SQL procedure successfully completed.

--//格式化如下:
SELECT "A1"."EMPNO" "EMPNO","A1"."ENAME" "ENAME","A1"."SAL" "SAL",
       ("A1"."C40")."B_DNAME" "C40.B_DNAME",("A1"."C40")."B_LOC" "C40.B_LOC"
  FROM (SELECT "A2"."EMPNO" "EMPNO",
       "A2"."ENAME" "ENAME","A2"."SAL" "SAL",
       (SELECT "T_DEPT"("A3"."DNAME","A3"."LOC") "T_DEPT(DEPT.DNAME,DEPT.LOC)"
  FROM "SCOTT"."DEPT" "A3"
 WHERE "A3"."DEPTNO" =  "A2"."DEPTNO") "C40"
  FROM "SCOTT"."EMP" "A2"
 WHERE "A2"."SAL"    >= 3000) "A1"

--//当然实际的情况估计很少开发愿意这样写代码。整体看采用拼接的方式还是不错的选择。
--//如果需要分开两个字段,可以使用substr+instr函数分开,例子:

SELECT empno
      ,ename
      ,sal
      ,SUBSTR (dname_loc,1,INSTR(dname_loc ,',')-1) dname
      ,SUBSTR (dname_loc,INSTR(dname_loc ,',')+1)    loc
  FROM (SELECT empno
              ,ename
              ,sal
              , (SELECT dname || ',' || loc FROM dept WHERE deptno = emp.deptno) dname_loc
          FROM emp
         WHERE sal >= 3000);

--//这种模式拼接2个还简单一些,如果多个字段相对就复杂了。
--//多个字段可以采用前面rpad函数格式化字符串,这样切分位置固定。

/* Formatted on 2024/12/28 10:28:25 (QP5 v5.277) */
SELECT empno
      ,ename
      ,sal
      ,SUBSTR ( dname_loc ,1 ,13) dname
      ,SUBSTR ( dname_loc ,14) loc
  FROM (SELECT empno
              ,ename
              ,sal
              ,(SELECT RPAD (dname ,13 ,' ')||loc FROM dept WHERE deptno = emp.deptno) dname_loc
          FROM emp
         WHERE sal >= 3000);


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/267265/viewspace-3061457/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
熟悉oracle相关技术,擅长sql优化,rman备份与恢复,熟悉linux shell编程。

注册时间:2008-01-03

  • 博文量
    4006
  • 访问量
    7969316