查询(SQL)是指数据库中用来获取数据的方式,它可搭配条件限制的子句(如 WHERE),排列顺序的子句(如 ORDER BY)等语句来获取查询结果。子查询是指嵌套在一个上层查询中的查询。上层的查询一般被称为父查询或外层查询。子查询的结果作为输入传递回“父查询”或“外部查询”。父查询将这个值结合到计算中,以便确定最后的输出。SQL 语言允许多层嵌套查询,即一个子查询中还可以嵌套其他子查询。同时,子查询可以出现在 SQL 语句中的各种子句中,比如 SELECT 语句,FROM 语句,WHERE 语句等。
子查询
在数据库中,子查询可以分成有依赖关系的子查询和没有依赖关系的子查询。有依赖关系的子查询是指该子查询的执行依赖了外部查询的变量,所以这种子查询通常会被计算多次。没有依赖关系的子查询是指该子查询的执行不依赖外部查询的变量, 这种子查询一般只需要计算一次。下面分别展示了一个没有依赖关系的子查询和一个有依赖关系的子查询。
obclient> create table t1(a int primary key, b int, c int);
Query OK, 0 rows affected (0.09 sec)
obclient> create table t2(a int primary key, b int, c int);
Query OK, 0 rows affected (0.06 sec)
-- 没有依赖关系的子查询
obclient> select * from t1 where t1.a in (select t2.a from t2);
Empty set (0.01 sec)
-- 有依赖关系的子查询,子查询中用到了外层查询变量t1.b
obclient> select * from t1 where t1.a in (select t2.a from t2 where t2.b = t1.b);
Empty set (0.01 sec)
子查询展开(subquery unnesting)
子查询展开是数据库的一种优化策略,它把一些子查询置于外层的父查询中,其实质是把某些子查询转化为等价的多表连接操作。这种策略带来的一个明显的好处就是,有些访问路径,连接方法和连接顺序可能被有效的利用,使得查询语句的层次尽可能的减少。下面展示了一个子查询展开的例子,即子查询被改写成了连接语句。
obclient> create table t1(a int primary key, b int, c int);
Query OK, 0 rows affected (0.09 sec)
obclient> create table t2(a int primary key, b int, c int);
Query OK, 0 rows affected (0.09 sec)
--- 有依赖关系的子查询被展开改写成连接
obclient> explain select * from t1 where t1.a in (select t2.b from t2 where t2.c = t1.c);
| ============================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
--------------------------------------------
|0 |MERGE JOIN | |9703 |215436|
|1 | TABLE SCAN |t1 |100000 |64066 |
|2 | SORT | |10001 |129621|
|3 | SUBPLAN SCAN |VIEW1|10001 |111242|
|4 | HASH DISTINCT| |10001 |109862|
|5 | TABLE SCAN |t2 |100000 |64066 |
============================================
Outputs & filters:
-------------------------------------
0 - output([t1.a], [t1.b], [t1.c]), filter(nil),
equal_conds([t1.a = VIEW1.t2.b], [VIEW1.t2.c = t1.c]), other_conds(nil)
1 - output([t1.c], [t1.a], [t1.b]), filter(nil),
access([t1.c], [t1.a], [t1.b]), partitions(p0)
2 - output([VIEW1.t2.b], [VIEW1.t2.c]), filter(nil), sort_keys([VIEW1.t2.b, ASC], [VIEW1.t2.c, ASC])
3 - output([VIEW1.t2.b], [VIEW1.t2.c]), filter(nil),
access([VIEW1.t2.b], [VIEW1.t2.c])
4 - output([t2.b], [t2.c]), filter(nil),
distinct([t2.b], [t2.c])
5 - output([t2.c], [t2.b]), filter(nil),
access([t2.c], [t2.b]), partitions(p0)