表表达式计算一个表。该表表达式包含一个FROM子句,该子句后面可以根据需要选用WHERE、GROUP BY和HAVING子句。
最简单的表表达式只是引用磁盘上的一个表,一个所谓的基本表,但是我们可以用更复杂的表表达式以多种方法修改或组合基本表。
表表达式里可选的WHERE、GROUP BY和HAVING子句指定一系列对源自FROM子句的表的转换操作。所有这些转换最后生成一个虚拟表,它提供行传递给选择列表计算查询的输出行。
FROM 子句从一个用逗号分隔的表引用列表中的一个或更多个其它表中生成一个表。
FROM table_reference [, table_reference [, ...]]
表引用可以是一个表名字(可能有模式限定)或者是一个生成的表, 例如子查询、一个JOIN结构或者这些东西的复杂组合。
如果在FROM子句中引用了多于一个表, 那么它们被交叉连接(即构造它们的行的笛卡尔积,见下文)。
FROM列表的结果是一个中间的虚拟表,该表可以进行由WHERE、GROUP BY和HAVING子句指定的转换,并最后生成全局的表表达式结果。
如果一个表引用是一个简单的表名字并且它是表继承层次中的父表,那么该表引用将产生该表和它的后代表中的行,除非你在该表名字前面放上ONLY关键字。
但是,这种引用只会产生出现在该命名表中的列 — 在子表中增加的列都会被忽略。
除了在表名前写ONLY,你可以在表名后面写上*来显式地指定要包括所有的后代表。没有实际的理由再继续使用这种语法,因为搜索后代表现在总是默认行为。不过,为了保持与旧版本的兼容性,仍然支持这种语法。
一个连接表是根据特定的连接类型的规则从两个其它表(真实表或生成表)中派生的表。目前支持内连接、外连接和交叉连接。
一个连接表的一般语法是:
T1 join_type T2 [ join_condition ]
所有类型的连接都可以被链在一起或者嵌套:T1和T2都可以是连接表。在JOIN子句周围可以使用圆括号来控制连接顺序。如果不使用圆括号,JOIN子句会从左至右嵌套。
连接类型
交叉连接
T1 CROSS JOIN T2
对来自于T1和T2的行的每一种可能的组合(即笛卡尔积),连接表将包含这样一行:它由所有T1里面的列后面跟着所有T2里面的列构成。如果两个表分别有 N 和 M 行,连接表将有 N * M 行。
FROM T1 CROSS JOIN T2等效于FROM T1 INNER JOIN T2 ON TRUE(见下文)。它也等效于FROM T1,T2。
注意:当多于两个表出现时,后一种等效并不严格成立,因为JOIN比逗号绑得更紧。例如FROM T1 CROSS JOIN T2 INNER JOIN T3 ON condition和FROM T1,T2 INNER JOIN T3 ON condition并不完全相同,因为第一种情况中的condition可以引用T1,但在第二种情况中却不行。
条件连接
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON boolean_expression
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( join column list )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2
INNER和OUTER对所有连接形式都是可选的。INNER是缺省;LEFT、RIGHT和FULL指示一个外连接。
连接条件在ON或USING子句中指定,或者用关键字NATURAL隐含地指定。连接条件决定来自两个源表中的哪些行是“匹配”的,这些我们将在后文详细解释。
可能的条件连接类型是:
INNER JOIN
对于 T1 的每一行 R1,生成的连接表都有一行对应 T2 中的每一个满足和 R1 的连接条件的行。
LEFT OUTER JOIN
首先,执行一次内连接。然后,为 T1 中每一个无法在连接条件上匹配 T2 里任何一行的行返回一个连接行,该连接行中 T2 的列用空值补齐。因此,生成的连接表里为来自 T1 的每一行都至少包含一行。
RIGHT OUTER JOIN
首先,执行一次内连接。然后,为 T2 中每一个无法在连接条件上匹配 T1 里任何一行的行返回一个连接行,该连接行中 T1 的列用空值补齐。因此,生成的连接表里为来自 T2 的每一行都至少包含一行。
FULL OUTER JOIN
首先,执行一次内连接。然后,为 T1 中每一个无法在连接条件上匹配 T2 里任何一行的行返回一个连接行,该连接行中 T2 的列用空值补齐。同样,为 T2 中每一个无法在连接条件上匹配 T1 里任何一行的行返回一个连接行,该连接行中 T1 的列用空值补齐。
ON子句是最常见的连接条件的形式:它接收一个和WHERE子句里用的一样的布尔值表达式。 如果两个分别来自T1和T2的行在ON表达式上运算的结果为真,那么它们就算是匹配的行。
USING是个缩写符号,它允许你利用特殊的情况:连接的两端都具有相同的连接列名。它接受共享列名的一个逗号分隔列表,并且为其中每一个共享列构造一个包含等值比较的连接条件。例如用USING (a, b)连接T1和T2会产生连接条件ON T1.a = T2.a AND T1.b = T2.b。
更进一步,JOIN USING的输出会废除冗余列:不需要把匹配上的列都打印出来,因为它们必须具有相等的值。不过JOIN ON会先产生来自T1的所有列,后面跟上所有来自T2的列;而JOIN USING会先为列出的每一个列对产生一个输出列,然后先跟上来自T1的剩余列,最后跟上来自T2的剩余列。
最后,NATURAL是USING的缩写形式:它形成一个USING列表, 该列表由那些在两个表里都出现了的列名组成。和USING一样,这些列只在输出表里出现一次。如果不存在公共列,NATURAL JOIN的行为将和JOIN ... ON TRUE一样产生交叉集连接。
注意:USING对于连接关系中的列改变是相当安全的,因为只有被列出的列会被组合成连接条件。NATURAL的风险更大,因为如果其中一个关系的模式改变会导致出现一个新的匹配列名,就会导致连接将新列也组合成连接条件。
为了解释这些问题,假设我们有一个表t1:
num | name
-----+------
1 | a
2 | b
3 | c
和t2:
num | value
-----+-------
1 | xxx
3 | yyy
5 | zzz
然后我们用不同的连接方式可以获得各种结果:
=> SELECT * FROM t1 CROSS JOIN t2;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
1 | a | 3 | yyy
1 | a | 5 | zzz
2 | b | 1 | xxx
2 | b | 3 | yyy
2 | b | 5 | zzz
3 | c | 1 | xxx
3 | c | 3 | yyy
3 | c | 5 | zzz
(9 rows)
=> SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
3 | c | 3 | yyy
(2 rows)
=> SELECT * FROM t1 INNER JOIN t2 USING (num);
num | name | value
-----+------+-------
1 | a | xxx
3 | c | yyy
(2 rows)
=> SELECT * FROM t1 NATURAL INNER JOIN t2;
num | name | value
-----+------+-------
1 | a | xxx
3 | c | yyy
(2 rows)
=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | 3 | yyy
(3 rows)
=> SELECT * FROM t1 LEFT JOIN t2 USING (num);
num | name | value
-----+------+-------
1 | a | xxx
2 | b |
3 | c | yyy
(3 rows)
=> SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
3 | c | 3 | yyy
| | 5 | zzz
(3 rows)
=> SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | 3 | yyy
| | 5 | zzz
(4 rows)
用ON指定的连接条件也可以包含与连接不直接相关的条件。这种功能可能对某些查询很有用,但是需要我们仔细想清楚。例如:
=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | |
(3 rows)
注意把限制放在WHERE子句中会产生不同的结果:
=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
(1 row)
这是因为放在ON子句中的一个约束在连接之前被处理,而放在WHERE子句中的一个约束是在连接之后被处理。
这对内连接没有关系,但是对于外连接会带来麻烦(外连接会出现内连接的效果)。