SQL 在Oracle中把标量子查询转换为复合子查询

SQL 在Oracle中把标量子查询转换为复合子查询,一个标量子查询中只允许返回一个值,你想绕过该限制。例如,你尝试执行如下所示的查询。

SQL 在Oracle中把标量子查询转换为复合子查询 问题描述

一个标量子查询中只允许返回一个值,你想绕过该限制。例如,你尝试执行如下所示的查询。

select e.deptno,
       e.ename,
       e.sal,
       (select d.dname,d.loc,sysdate today
          from dept d
         where e.deptno=d.deptno)
  from emp e

上述查询会因为报错而无法执行,因为 SELECT 列表里的子查询只允许返回一个值。

SQL 在Oracle中把标量子查询转换为复合子查询 解决方案

诚然,上述问题似乎有些不切实际,因为只要把 EMP 表和 DEPT 表连接起来,我们就能方便地从 DEPT 表中提取出任意值。但是,我的本意是希望你关注技巧,并认识到本问题在某些场景下有其实用性。当在 SELECT 中放入了另一个 SELECT(标量子查询)时,要想绕过只返回一个值的限制,就需要利用 Oracle 的对象类型。我们可以定义一个拥有多个属性的对象,然后把它作为一个单独的实体来处理,并且可以访问其中的每一个属性。实际上,我们并没有真正地打破那个规则。它仍然只返回了一个值,只不过该返回值是一个对象,它里面包含了许多属性。
本解决方案使用到了下面的对象类型。

create type generic_obj
    as object (
    val1 varchar2(10),
    val2 varchar2(10),
    val3 date
);

有了以上对象类型,就可以执行下面的查询。

 1  select x.deptno,
 2         x.ename,
 3         x.multival.val1 dname,
 4         x.multival.val2 loc,
 5         x.multival.val3 today
 6    from (
 7  select e.deptno,
 8         e.ename,
 9         e.sal,
10         (select generic_obj(d.dname,d.loc,sysdate+1)
11            from dept d
12           where e.deptno=d.deptno) multival
13    from emp e
14         ) x
 
DEPTNO ENAME      DNAME      LOC        TODAY
------ ---------- ---------- ---------- -----------
    20 SMITH      RESEARCH   DALLAS     12-SEP-2005
    30 ALLEN      SALES      CHICAGO    12-SEP-2005
    30 WARD       SALES      CHICAGO    12-SEP-2005
    20 JONES      RESEARCH   DALLAS     12-SEP-2005
    30 MARTIN     SALES      CHICAGO    12-SEP-2005
    30 BLAKE      SALES      CHICAGO    12-SEP-2005
    10 CLARK      ACCOUNTING NEW YORK   12-SEP-2005
    20 SCOTT      RESEARCH   DALLAS     12-SEP-2005
    10 KING       ACCOUNTING NEW YORK   12-SEP-2005
    30 TURNER     SALES      CHICAGO    12-SEP-2005
    20 ADAMS      RESEARCH   DALLAS     12-SEP-2005
    30 JAMES      SALES      CHICAGO    12-SEP-2005
    20 FORD       RESEARCH   DALLAS     12-SEP-2005
    10 MILLER     ACCOUNTING NEW YORK   12-SEP-2005

SQL 在Oracle中把标量子查询转换为复合子查询 扩展知识

本解决方案的关键在于使用上述对象的构造函数(默认情况下构造函数和对象同名)。因为对象本身是一个标量值,它并不会违反标量子查询的规则,如下所示。

select e.deptno,
       e.ename,
       e.sal,
       (select generic_obj(d.dname,d.loc,sysdate-1)
          from dept d
         where e.deptno=d.deptno) multival
  from emp e
 
DEPTNO ENAME    SAL MULTIVAL(VAL1, VAL2, VAL3)
------ ------ ----- -------------------------------------------------------
    20 SMITH    800 GENERIC_OBJ('RESEARCH', 'DALLAS', '12-SEP-2005')
    30 ALLEN   1600 GENERIC_OBJ('SALES', 'CHICAGO', '12-SEP-2005')
    30 WARD    1250 GENERIC_OBJ('SALES', 'CHICAGO', '12-SEP-2005')
    20 JONES   2975 GENERIC_OBJ('RESEARCH', 'DALLAS', '12-SEP-2005')
    30 MARTIN  1250 GENERIC_OBJ('SALES', 'CHICAGO', '12-SEP-2005')
    30 BLAKE   2850 GENERIC_OBJ('SALES', 'CHICAGO', '12-SEP-2005')
    10 CLARK   2450 GENERIC_OBJ('ACCOUNTING', 'NEW YORK', '12-SEP-2005')
    20 SCOTT   3000 GENERIC_OBJ('RESEARCH', 'DALLAS', '12-SEP-2005')
    10 KING    5000 GENERIC_OBJ('ACCOUNTING', 'NEW YORK', '12-SEP-2005')
    30 TURNER  1500 GENERIC_OBJ('SALES', 'CHICAGO', '12-SEP-2005')
    20 ADAMS   1100 GENERIC_OBJ('RESEARCH', 'DALLAS', '12-SEP-2005')
    30 JAMES    950 GENERIC_OBJ('SALES', 'CHICAGO', '12-SEP-2005')
    20 FORD    3000 GENERIC_OBJ('RESEARCH', 'DALLAS', '12-SEP-2005')
    10 MILLER  1300 GENERIC_OBJ('ACCOUNTING', 'NEW YORK', '12-SEP-2005')

接下来,只要把上述查询包裹进一个内嵌视图里,并提取各个属性即可。

 重要提示:Oracle 不同于其他数据库,内嵌视图不一定非要有别名。但对于本例而言,我们必须为上述内嵌视图指定一个别名。否则,我们就无法访问对象的属性。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程