1026.查询-from子句2

你可以给一个表或复杂的表引用指定一个临时的名字,用于剩下的查询中引用那些派生的表。这被叫做表别名。

要创建一个表别名,我们可以写:

FROM table_reference AS alias

或者

FROM table_reference alias

AS关键字是可选的。别名可以是任意标识符。

表别名的典型应用是给长表名赋予比较短的标识符,好让连接子句更易读。例如:

SELECT * FROM some_very_long_table_name s JOIN another_fairly_long_name a ON s.id = a.num;

到这里,别名成为当前查询的表引用的新名称 — 我们不再能够用该表最初的名字引用它了。因此,下面的用法是不合法的:

SELECT * FROM my_table AS m WHERE my_table.a > 5;    -- 错误

表别名主要用于简化符号,但是当把一个表连接到它自身时必须使用别名,例如:

SELECT * FROM people AS mother JOIN people AS child ON mother.id = child.mother_id;

此外,如果一个表引用是一个子查询,则必须要使用一个别名

圆括弧用于解决歧义。在下面的例子中,第一个语句将把别名b赋给my_table的第二个实例,但是第二个语句把别名赋给连接的结果:

SELECT * FROM my_table AS a CROSS JOIN my_table AS b ...

SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ...

另外一种给表指定别名的形式是给表的列赋予临时名字,就像给表本身指定别名一样:

FROM table_reference [AS] alias ( column1 [, column2 [, ...]] )

如果指定的列别名比表里实际的列少,那么剩下的列就没有被重命名。这种语法对于自连接或子查询特别有用。

如果用这些形式中的任何一种给一个JOIN子句的输出附加了一个别名, 那么该别名就在JOIN的作用下隐去了其原始的名字。例如:

SELECT a.* FROM my_table AS a JOIN your_table AS b ON ...

是合法 SQL,但是:

SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c

是不合法的:表别名a在别名c外面是看不到的。

 

子查询

子查询指定了一个派生表,它必须被包围在圆括弧里并且必须被赋予一个表别名,例如:

FROM (SELECT * FROM table1) AS alias_name

这个例子等效于FROM table1 AS alias_name。更有趣的情况是在子查询里面有分组或聚集的时候, 子查询不能被简化为一个简单的连接。

一个子查询也可以是一个VALUES列表:

FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow'))

     AS names(first, last)

再次的,这里要求一个表别名。VALUES列表中的列分配别名是可选的,但是选择这样做是一个好习惯。

 

表函数

表函数是那些生成一个行集合的函数,这个集合可以是由基本数据类型(标量类型)组成,也可以是由复合数据类型(表行)组成。

它们的用法类似一个表、视图或者在查询的FROM子句里的子查询。表函数返回的列可以像一个表列、视图或者子查询那样被包含在SELECT、JOIN或WHERE子句里。

也可以使用ROWS FROM语法将平行列返回的结果组合成表函数; 这种情况下结果行的数量是最大一个函数结果的数量,较小的结果会用空值来填充。

function_call [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]

ROWS FROM( function_call [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]

如果指定了WITH ORDINALITY子句,一个额外的 bigint类型的列将会被增加到函数的结果列中。这个列对 函数结果集的行进行编号,编号从 1 开始(这是对 SQL 标准语法 UNNEST ... WITH ORDINALITY的一般化)。默认情 况下,序数列被称为ordinality,但也可以通过使用一个 AS子句给它分配一个不同的列名。

调用特殊的表函数UNNEST可以使用任意数量的数组参数, 它会返回对应的列数,就好像在每一个参数上单独调用 UNNEST并且使用 ROWS FROM结构把它们组合起来。

UNNEST( array_expression [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]

如果没有指定table_alias,该函数名将被用作 表名。在ROWS FROM()结构的情况中,会使用第一个函数名。

如果没有提供列的别名,那么对于一个返回基数据类型的函数,列名也与该函数 名相同。对于一个返回组合类型的函数,结果列会从该类型的属性得到名称。

例子:

CREATE TABLE foo (fooid int, foosubid int, fooname text);

CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$

    SELECT * FROM foo WHERE fooid = $1;

$$ LANGUAGE SQL;

 

SELECT * FROM getfoo(1) AS t1;

SELECT * FROM foo

    WHERE foosubid IN (

                        SELECT foosubid

                        FROM getfoo(foo.fooid) z

                        WHERE z.fooid = foo.fooid

                      );

CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);

SELECT * FROM vw_getfoo;

有时侯,定义一个能够根据它们被调用方式返回不同列集合的表函数是很有用的。为了支持这些,表函数可以被声明为返回伪类型record。 如果在查询里使用这样的函数,那么我们必须在查询中指定所预期的行结构,这样系统才知道如何分析和规划该查询。这种语法是这样的:

function_call [AS] alias (column_definition [, ... ])

function_call AS [alias] (column_definition [, ... ])

ROWS FROM( ... function_call AS (column_definition [, ... ]) [, ... ] )

在没有使用ROWS FROM()语法时, column_definition列表会取代无法附着在 FROM项上的列别名列表,列定义中的名称就起到列别名的作用。 在使用ROWS FROM()语法时, 可以为每一个成员函数单独附着一个 column_definition列表;或者在只有一个成员 函数并且没有WITH ORDINALITY子句的情况下,可以在 ROWS FROM()后面写一个 column_definition列表来取代一个列别名列表。

 

考虑下面的例子:

SELECT *

    FROM dblink('dbname=mydb', 'SELECT proname, prosrc FROM pg_proc')

      AS t1(proname name, prosrc text)

    WHERE proname LIKE 'bytea%';

dblink函数(dblink模块的一部分)执行一个远程的查询。它被声明为返回record,因为它可能会被用于任何类型的查询。 实际的列集必须在调用它的查询中指定,这样分析器才知道类似*这样的东西应该扩展成什么样子。

 

LATERAL子查询

可以在出现于FROM中的子查询前放置关键词LATERAL。这允许它们引用前面的FROM项提供的列(如果没有LATERAL,每一个子查询将被独立计算,并且因此不能被其他FROM项交叉引用)。

出现在FROM中的表函数的前面也可以被放上关键词LATERAL,但对于函数该关键词是可选的,在任何情况下函数的参数都可以包含对前面的FROM项提供的列的引用。

一个LATERAL项可以出现在FROM列表顶层,或者出现在一个JOIN树中。在后一种情况下,如果它出现在JOIN的右部,那么它也可以引用 在JOIN左部的任何项。

如果一个FROM项包含LATERAL交叉引用,计算过程如下:对于提供交叉引用列的FROM项的每一行,或者多个提供这些列的多个FROM项的行集合,LATERAL项将被使用该行或者行集中的列值进行计算。得到的结果行将和它们被计算出来的行进行正常的连接。对于来自这些列的源表的每一行或行集,该过程将重复。

 

LATERAL的一个简单例子:

SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;

这不是非常有用,因为它和一种更简单的形式得到的结果完全一样:

SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;

在必须要使用交叉引用列来计算那些即将要被连接的行时,LATERAL是最有用的。一种常用的应用是为一个返回集合的函数提供一个参数值。例如,假设vertices(polygon)返回一个多边形的顶点集合,我们可以这样标识存储在一个表中的多边形中靠近的顶点:

SELECT p1.id, p2.id, v1, v2

FROM polygons p1, polygons p2,

     LATERAL vertices(p1.poly) v1,

     LATERAL vertices(p2.poly) v2

WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

这个查询也可以被写成:

SELECT p1.id, p2.id, v1, v2

FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,

     polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2

WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

或者写成其他几种等价的公式(正如以上提到的,LATERAL关键词在这个例子中并不是必不可少的,但是我们在这里使用它是为了使表述更清晰)。

有时候也会很特别地把LEFT JOIN放在一个LATERAL子查询的前面,这样即使LATERAL子查询对源行不产生行,源行也会出现在结果中。

例如,如果get_product_names()返回一个制造商制造的产品的名字,但是某些制造商在我们的表中目前没有制造产品,我们可以找出哪些制造商是这样:

SELECT m.name

FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true

WHERE pname IS NULL;

posted @ 2021-01-06 23:42  bufuzhou  阅读(158)  评论(0编辑  收藏  举报