什么是快乐星球?什么又是“标量子查询”呢?

Posted bisal

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是快乐星球?什么又是“标量子查询”呢?相关的知识,希望对你有一定的参考价值。

现在有个很火的歌词,“什么是快乐星球?”,那个劲儿,其实还很难拿捏,

同理地,以前总是在资料或者培训中听到“标量子查询”,好像很神秘的一个概念,什么是“标量子查询”?

其实从形式上讲,标量子查询就是在查询语句select和from之间的子查询,这就决定了他的返回值肯定只能是1条,如果是多条,就可能提示“ORA-01427:单行子查询返回多个行的错误”。

这是一个标量子查询的例子,

SQL> select /*+ gather_plan_statistics */ e.first_name, e.salary,
           (select d.department_name from departments d
      where d.department_id = e.department_id) d_name
       from employees e; 

他的执行计划,

从执行计划,可以看到,主表employees通过连接列(department_id)传值给子查询中的表(departments),上述执行计划中第二步的* - :B1就是传值,而且传值12次。

之所以是12次,因为主表的连接列基数是12,

SQL> select count(distinct nvl(department_id,0)) from employees;
COUNT(DISTINCTNVL(DEPARTMENT_ID,0))
-----------------------------------
         12

从这个过程,我们能知道,如果必须使用标量子查询,要为子查询的连接列创建索引,毕竟主表连接列基数是多少,子查询就会被执行多少次,因此能推导出,如果主表的数据量很少,或者主表的连接列基数很低,子查询的连接列创建了索引,是可以使用标量子查询的。反之,主表返回的数据量很多,主表的连接列基数很高,甚至子查询的连接列没索引,应该避免使用标量子查询。

为了避免使用标量子查询,一般可以改写为外连接,

SQL> select /*+ gather_plan_statistics */ e.first_name, e.salary, d.department_name
       from employees e 
       left join departments d
         on e.department_id = d.department_id;

他的执行计划,

可以看到,原来的子查询改为了全表扫描,但是仅需要扫描一次,和主表做哈希外连接。

之所以是外连接,因为每次主表传值给子查询,但未必在子查询中就存在对应的记录,但是如果子查询的连接列就是主表的外键,或者业务上就是1:1的关系(这里用到了落落老师在培训和书中提到的数量关系),外连接可以改为内连接,他俩是等价的,如本例中,employees共107条,

SQL> select count(*) from employees;
  COUNT(*)
----------
       107

如果采用外连接,返回107条,

SQL> select count(*) from employees e left join departments d on e.department_id = d.department_id;
  COUNT(*)
----------
       107

如果采用内连接,只会返回106条,说明在employees中有一条记录在departments是找不到的,

SQL> select count(*) from employees e join departments d on e.department_id = d.department_id;
  COUNT(*)
----------
       106

这个例子中,标量子查询departments和主表employees是1:N的关系,因此改为外连接的时候,不需要去重。

如果是这种,标量子查询和主表是N:1的关系,

SQL> select d.department_name,
            (select max(e.salary) from employees e where e.department_id = d.department_id) max_salary
       from departments d;
DEPARTMENT_NAME       MAX_SALARY
--------------------- ----------
Shipping              8200
Human Resources       6500
Accounting            12008
Executive             24000
Purchasing            11000
Public Relations      10000
Administration        4400
Marketing             13000
IT                    9000
Finance               12008
Sales                 14000
IT Support
Operations
Payroll
Construction
Government Sales
Retail Sales
Contracting
Recruiting
Control And Credit
NOC
Treasury
Manufacturing
Corporate Tax
IT Helpdesk
Shareholder Services
Benefits
27 rows selected.

改成外连接,需要对标量子查询做去重(group by),可以先去重,再做关联,如下所示,

SQL> select /*+ gather_plan_statistics */ d.department_name, max_salary
       from departments d
       left join (select max(salary) max_salary, department_id from employees group by department_id) e
         on e.department_id = d.department_id;
DEPARTMENT_NAME        MAX_SALARY
---------------------- ----------
Shipping                8200
Human Resources         6500
Accounting              12008
Executive               24000
Purchasing              11000
Public Relations        10000
Administration          4400
Marketing               13000
IT                      9000
Finance                 12008
Sales                   14000
IT Support
Operations
Payroll
Construction
Government Sales
Retail Sales
Contracting
Recruiting
Control And Credit
NOC
Treasury
Manufacturing
Corporate Tax
IT Helpdesk
Shareholder Services
Benefits
27 rows selected.

他的执行计划,

或者先关联,再做去重,如下所示,

SQL>  select /*+ gather_plan_statistics */ d.department_name, max(e.salary)
        from departments d
        left join employees e
          on e.department_id = d.department_id
       group by d.department_name;
DEPARTMENT_NAME       MAX(E.SALARY)
--------------------- -------------
Sales                  14000
Recruiting
Control And Credit
IT Support
Government Sales
Retail Sales
Corporate Tax
Marketing              13000
IT Helpdesk
Purchasing             11000
Administration         4400
Contracting
NOC
Executive              24000
IT                     9000
Finance                12008
Shipping               8200
Public Relations       10000
Payroll
Shareholder Services
Benefits
Human Resources        6500
Accounting             12008
Operations
Construction
Treasury
Manufacturing
27 rows selected.

他的执行计划,

从执行计划看,这两种写法,成本几乎相同。

理解标量子查询的适用场景,以及改成外连接是否需要去重的意义,是对待这种标量子查询的关键。

近期更新的文章:

VMWare增加磁盘空间的操作

曾经运维生涯中的几个“最”

一条半连接SQL的优化过程

如何找到隐式转换的SQL?

为什么日期不建议使用VARCHAR2或者NUMBER?

文章分类和索引:

公众号700篇文章分类和索引

以上是关于什么是快乐星球?什么又是“标量子查询”呢?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server的优化器会缓存标量子查询结果集吗

Pytorch Note 快乐星球

SQL优化:慎用标量子查询,改用left join提升查询效率

通过三层标量子查询传递数据

更新后触发,错误:标量子查询只允许返回单行

mysql 标量子查询和非法子查询