SELECT
SELECT、TABLE、WITH — 从表或视图中检索行
概要
[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
[ * | expression [ [ AS ] output_name ] [, ...] ]
[ FROM from_item [, ...] ]
[ WHERE condition ]
[ GROUP BY [ ALL | DISTINCT ] grouping_element [, ...] ]
[ HAVING condition ]
[ WINDOW window_name AS ( window_definition ) [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start [ ROW | ROWS ] ]
[ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]
[ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ]
where from_item can be one of:
[ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
[ TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] ]
[ LATERAL ] ( select ) [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
[ LATERAL ] function_name ( [ argument [, ...] ] )
[ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
[ LATERAL ] function_name ( [ argument [, ...] ] ) [ AS ] alias ( column_definition [, ...] )
[ LATERAL ] function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )
[ LATERAL ] ROWS FROM( function_name ( [ argument [, ...] ] ) [ AS ( column_definition [, ...] ) ] [, ...] )
[ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
from_item join_type from_item { ON join_condition | USING ( join_column [, ...] ) [ AS join_using_alias ] }
from_item NATURAL join_type from_item
from_item CROSS JOIN from_item
and grouping_element can be one of:
( )
expression
( expression [, ...] )
ROLLUP ( { expression | ( expression [, ...] ) } [, ...] )
CUBE ( { expression | ( expression [, ...] ) } [, ...] )
GROUPING SETS ( grouping_element [, ...] )
and with_query is:
with_query_name [ ( column_name [, ...] ) ] AS [ [ NOT ] MATERIALIZED ] ( select | values | insert | update | delete )
[ SEARCH { BREADTH | DEPTH } FIRST BY column_name [, ...] SET search_seq_col_name ]
[ CYCLE column_name [, ...] SET cycle_mark_col_name [ TO cycle_mark_value DEFAULT cycle_mark_default ] USING cycle_path_col_name ]
TABLE [ ONLY ] table_name [ * ]
描述
SELECT
从零个或多个表中检索行。SELECT
的一般处理过程如下
WITH
列表中的所有查询都已计算。这些查询实际上充当临时表,可以在FROM
列表中引用。在FROM
中多次引用的WITH
查询仅计算一次,除非使用NOT MATERIALIZED
另行指定。(请参见下面的 WITH 子句。)计算
FROM
列表中的所有元素。(FROM
列表中的每个元素都是一个真实或虚拟表。)如果在FROM
列表中指定了多个元素,则它们将相互交叉连接。(请参见下面的 FROM 子句。)如果指定了
WHERE
子句,则会从输出中删除所有不满足条件的行。(请参见下面的 WHERE 子句。)如果指定了
GROUP BY
子句,或者如果有聚合函数调用,则输出将合并到匹配一个或多个值的各组行中,并计算聚合函数的结果。如果存在HAVING
子句,则它将删除不满足给定条件的组。(请参见下面的 GROUP BY 子句 和 HAVING 子句。)虽然查询输出列通常在下一步中计算,但它们也可以在GROUP BY
子句中按名称或序数引用。使用
SELECT
输出表达式为每个选定的行或行组计算实际的输出行。(请参见下面的 SELECT 列表。)SELECT DISTINCT
从结果中删除重复行。SELECT DISTINCT ON
删除与所有指定表达式匹配的行。SELECT ALL
(默认值)将返回所有候选行,包括重复行。(请参见下面的 DISTINCT 子句。)使用运算符
UNION
、INTERSECT
和EXCEPT
,可以将多个SELECT
语句的输出组合成一个结果集。UNION
运算符返回一个或两个结果集中存在的所有行。INTERSECT
运算符返回两个结果集中严格存在的所有行。EXCEPT
运算符返回第一个结果集中存在但第二个结果集中不存在的行。 在所有这三种情况下,除非指定ALL
,否则将消除重复行。 可以添加噪声词DISTINCT
以明确指定消除重复行。 请注意,即使ALL
是SELECT
本身的默认值,但DISTINCT
是此处的默认行为。(请参见以下内容:UNION 子句、INTERSECT 子句 和 EXCEPT 子句。)如果指定了
ORDER BY
子句,则按指定顺序对返回的行进行排序。 如果未给出ORDER BY
,则按系统发现生成最快的顺序返回行。(请参见以下内容:ORDER BY 子句。)如果指定了
LIMIT
(或FETCH FIRST
)或OFFSET
子句,则SELECT
语句仅返回结果行的一个子集。(请参见以下内容:LIMIT 子句。)如果指定了
FOR UPDATE
、FOR NO KEY UPDATE
、FOR SHARE
或FOR KEY SHARE
,则SELECT
语句将选定的行锁定以防止并发更新。(请参见以下内容:锁定子句。)
您必须对SELECT
命令中使用的每列拥有SELECT
权限。 使用FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
或FOR KEY SHARE
还需要UPDATE
权限(至少对于每个选定表的某一列)。
参数
WITH
子句
WITH
子句允许您指定一个或多个子查询,这些子查询可以在主查询中通过名称进行引用。子查询在主查询持续期间实际上充当临时表或视图。每个子查询可以是SELECT
、TABLE
、VALUES
、INSERT
、UPDATE
或DELETE
语句。在WITH
中编写数据修改语句(INSERT
、UPDATE
或DELETE
)时,通常会包含一个RETURNING
子句。主查询读取的临时表形成的是RETURNING
的输出,而不是语句修改的基础表。如果省略了RETURNING
,该语句仍会被执行,但它不会产生输出,因此主查询无法将其作为表进行引用。
必须为每个WITH
查询指定一个名称(不带模式限定)。还可以选择指定列名称列表;如果省略此列表,则列名称将从子查询中推断出来。
如果指定了RECURSIVE
,它允许SELECT
子查询通过名称引用自身。此类子查询必须具有以下形式
non_recursive_term UNION [ ALL | DISTINCT ] recursive_term
其中递归自引用必须出现在UNION
的右侧。每个查询只允许有一个递归自引用。不支持递归数据修改语句,但您可以在数据修改语句中使用递归SELECT
查询的结果。有关示例,请参见第 7.8 节。
RECURSIVE
的另一个作用是WITH
查询不必按顺序排列:一个查询可以引用列表中后面的另一个查询。(但是,未实现循环引用或相互递归。)如果没有RECURSIVE
,WITH
查询只能引用WITH
列表中较早的同级WITH
查询。
当WITH
子句中有多个查询时,RECURSIVE
应仅在WITH
之后立即编写一次。它适用于WITH
子句中的所有查询,尽管它对不使用递归或前向引用的查询没有影响。
可选的SEARCH
子句计算一个搜索序列列,该列可用于按广度优先或深度优先顺序对递归查询的结果进行排序。提供的列名列表指定用于跟踪已访问行的行键。一个名为*search_seq_col_name
*的列将添加到WITH
查询的结果列列表中。可以在外部查询中对该列进行排序以实现相应的排序。有关示例,请参见第 7.8.2.1 节。
可选的CYCLE
子句用于检测递归查询中的循环。提供的列名列表指定用于跟踪已访问行的行键。一个名为*cycle_mark_col_name
的列将添加到WITH
查询的结果列列表中。当检测到循环时,此列将设置为cycle_mark_value
,否则设置为cycle_mark_default
。此外,当检测到循环时,递归并集的处理将停止。cycle_mark_value
和cycle_mark_default
必须是常量,并且它们必须可强制转换为公共数据类型,并且数据类型必须具有不等式运算符。(SQL 标准要求它们是布尔常量或字符串,但 PostgreSQL 并不需要。)默认情况下,使用TRUE
和FALSE
(类型为boolean
)。此外,一个名为cycle_path_col_name
*的列将添加到WITH
查询的结果列列表中。此列在内部用于跟踪已访问的行。有关示例,请参见第 7.8.2.2 节。
SEARCH
和CYCLE
子句都仅对递归WITH
查询有效。*with_query
*必须是两个SELECT
(或等效)命令(无嵌套UNION
)的UNION
(或UNION ALL
)。如果同时使用这两个子句,则SEARCH
子句添加的列将出现在CYCLE
子句添加的列之前。
主查询和WITH
查询在概念上是同时执行的。这意味着WITH
中的数据修改语句的效果无法从查询的其他部分看到,只能通过读取其RETURNING
输出看到。如果两个这样的数据修改语句尝试修改同一行,则结果不确定。
WITH
查询的一个关键属性是,即使主查询引用它们多次,它们通常也只在主查询执行一次。特别是,无论主查询是否读取其所有或任何输出,数据修改语句都保证只执行一次。
但是,可以将WITH
查询标记为NOT MATERIALIZED
以取消此保证。在这种情况下,WITH
查询可以折叠到主查询中,就像它是主查询FROM
子句中的一个简单子SELECT
一样。如果主查询多次引用该WITH
查询,这会导致重复计算;但如果每个这样的使用只需要WITH
查询的总输出的几行,则NOT MATERIALIZED
可以通过允许联合优化查询来提供净节省。如果NOT MATERIALIZED
附加到递归或非无副作用(即不是不包含任何易失函数的普通SELECT
)的WITH
查询,则将其忽略。
默认情况下,如果在主查询的FROM
子句中只使用一次,则无副作用的WITH
查询将折叠到主查询中。这允许在语义上应该是不可见的的情况下对两个查询级别进行联合优化。但是,可以通过将WITH
查询标记为MATERIALIZED
来防止这种折叠。例如,如果WITH
查询用作优化围栏以防止计划程序选择错误的计划,这可能很有用。PostgreSQLv12 之前的版本从未进行过这样的折叠,因此为旧版本编写的查询可能依赖WITH
作为优化围栏。
有关其他信息,请参见第 7.8 节。
FROM
子句
FROM
子句为SELECT
指定一个或多个源表。如果指定了多个源,则结果是所有源的笛卡尔积(交叉连接)。但通常会添加限定条件(通过WHERE
)将返回的行限制为笛卡尔积的一个小子集。
FROM
子句可以包含以下元素
table_name
现有表或视图的名称(可选模式限定)。如果在表名前指定了
ONLY
,则只扫描该表。如果未指定ONLY
,则扫描该表及其所有后代表(如果有)。另外,可以在表名后指定*
以明确表示包括后代表。alias
包含别名的
FROM
项的替代名称。别名用于简化或消除自连接(同一表被多次扫描)的歧义。提供别名时,它会完全隐藏表或函数的实际名称;例如,给定FROM foo AS f
,SELECT
的其余部分必须将此FROM
项引用为f
,而不是foo
。如果编写了别名,还可以编写列别名列表,以便为表的某一列或多列提供替代名称。TABLESAMPLE
sampling_method
(argument
[, ...] ) [ REPEATABLE (seed
) ]table_name
后面的TABLESAMPLE
子句表示应使用指定的sampling_method
来检索该表中的一行子集。此采样先于任何其他筛选器的应用,例如WHERE
子句。标准 PostgreSQL 发行版包括两种采样方法,BERNOULLI
和SYSTEM
,其他采样方法可以通过扩展安装到数据库中。BERNOULLI
和SYSTEM
采样方法各接受一个表示要采样的表分数的单个参数
,以 0 到 100 之间的百分比表示。此参数可以是任何real
值表达式。(其他采样方法可能接受更多或不同的参数。)这两种方法各返回一个随机选择的表样本,该样本将包含大约指定百分比的表行。BERNOULLI
方法扫描整个表,并以指定概率独立选择或忽略各个行。SYSTEM
方法执行块级采样,每个块都有指定的选择机会;将返回每个选定块中的所有行。当指定较小的采样百分比时,SYSTEM
方法明显快于BERNOULLI
方法,但它可能会由于聚类效应而返回表中不太随机的样本。可选的
REPEATABLE
子句指定用于在采样方法中生成随机数的种子
编号或表达式。种子值可以是任何非空浮点值。如果表在此期间未更改,则指定相同种子和参数
值的两个查询将选择表的相同样本。但不同的种子值通常会产生不同的样本。如果未给出REPEATABLE
,则会基于系统生成的种子为每个查询选择一个新的随机样本。请注意,一些附加采样方法不接受REPEATABLE
,并且在每次使用时总是会生成新的样本。select
子
SELECT
可以出现在FROM
子句中。这就像它的输出被创建为此单个SELECT
命令持续时间的临时表。请注意,子SELECT
必须用括号括起来,并且可以像表一样提供别名。此处还可以使用VALUES
命令。with_query_name
通过编写
WITH
查询的名称来引用该查询,就像查询名称是表名称一样。(事实上,WITH
查询会隐藏任何同名真实表,以用于主查询。如果需要,可以通过对表名称进行架构限定来引用同名真实表。)可以像对表一样提供别名。function_name
函数调用可以出现在
FROM
子句中。(这对于返回结果集的函数特别有用,但可以使用任何函数。)这就像函数的输出被创建为临时表,用于此单个SELECT
命令的持续时间。如果函数的结果类型是复合类型(包括具有多个OUT
参数的函数的情况),则每个属性都成为隐式表中的一个单独列。当将可选
WITH ORDINALITY
子句添加到函数调用时,将向函数的结果列附加一个类型为bigint
的附加列。此列对函数结果集的行进行编号,从 1 开始。默认情况下,此列名为ordinality
。可以像对表一样提供别名。如果编写了别名,还可以编写列别名列表,以提供函数复合返回类型的某个或多个属性的替代名称,包括序数列(如果存在)。
可以通过使用
ROWS FROM( ... )
将多个函数调用组合成单个FROM
子句项。此类项的输出是来自每个函数的第一行、然后是来自每个函数的第二行等的连接。如果某些函数产生的行少于其他函数,则用空值替换缺失数据,以便返回的总行数始终与产生最多行的函数相同。如果已将函数定义为返回
record
数据类型,则必须存在别名或关键字AS
,后跟形式为(
的列定义列表。列定义列表必须与函数返回的实际列数和类型相匹配。column_name
data_type
[, ... ])在使用
ROWS FROM( ... )
语法时,如果某个函数需要列定义列表,则最好在ROWS FROM( ... )
内的函数调用后放置列定义列表。只有在只有一个函数且没有WITH ORDINALITY
子句时,才能在ROWS FROM( ... )
构造后放置列定义列表。要将
ORDINALITY
与列定义列表一起使用,必须使用ROWS FROM( ... )
语法,并将列定义列表放入ROWS FROM( ... )
中。join_type
之一
[ INNER ] JOIN
LEFT [ OUTER ] JOIN
RIGHT [ OUTER ] JOIN
FULL [ OUTER ] JOIN
对于
INNER
和OUTER
联接类型,必须指定联接条件,即ON
、join_condition
USING (
或join_column
[, ...])NATURAL
中的一个。请参阅以下内容以了解含义。JOIN
子句组合两个FROM
项,为方便起见,我们将它们称为 “表”,尽管实际上它们可以是任何类型的FROM
项。必要时使用括号来确定嵌套顺序。如果没有括号,JOIN
将从左到右嵌套。在任何情况下,JOIN
的绑定都比分隔FROM
列表项的逗号更紧密。所有JOIN
选项都只是符号上的便利,因为它们不会做任何使用纯FROM
和WHERE
无法完成的事情。LEFT OUTER JOIN
返回限定笛卡尔积中的所有行(即,通过其联接条件的所有组合行),以及左手表中每一行的副本,而右手表中没有通过联接条件的行。通过为右手列插入空值,将此左手行扩展到联接表的全宽。请注意,在确定哪些行具有匹配项时,只考虑JOIN
子句自己的条件。外部条件随后应用。相反,
RIGHT OUTER JOIN
返回所有联接行,以及每一行不匹配的右手行(在左侧用空值扩展)。这只是符号上的便利,因为你可以通过切换左右表将其转换为LEFT OUTER JOIN
。FULL OUTER JOIN
返回所有联接行,以及每一行不匹配的左手行(在右侧用空值扩展),以及每一行不匹配的右手行(在左侧用空值扩展)。ON
join_condition
join_condition
是一个表达式,其结果为boolean
类型的值(类似于WHERE
子句),它指定联接中的哪些行被认为匹配。USING (
join_column
[, ...] ) [ ASjoin_using_alias
]形式为
USING ( a, b, ... )
的子句是ON left_table.a = right_table.a AND left_table.b = right_table.b ...
的简写。此外,USING
意味着联接输出中只会包含每一对等效列中的一个,而不是两个。如果指定了
join_using_alias
名称,它将为联接列提供表别名。只有USING
子句中列出的联接列可以通过此名称寻址。与常规alias
不同,这不会向查询的其余部分隐藏联接表的名称。此外,与常规alias
不同,您无法编写列别名列表——联接列的输出名称与它们在USING
列表中显示的名称相同。NATURAL
NATURAL
是一个简写,表示USING
列表,其中提到了两张表中所有具有匹配名称的列。如果没有公共列名称,NATURAL
等效于ON TRUE
。CROSS JOIN
CROSS JOIN
等效于INNER JOIN ON (TRUE)
,即,不会通过限定条件删除任何行。它们生成一个简单的笛卡尔积,与您从在FROM
的顶级列出两张表得到的相同结果,但受联接条件(如果有)的限制。LATERAL
LATERAL
关键字可以出现在子SELECT
FROM
项之前。这允许子SELECT
引用出现在FROM
列表中其之前的FROM
项的列。(如果没有LATERAL
,每个子SELECT
都将独立评估,因此无法交叉引用任何其他FROM
项。)LATERAL
也可以出现在函数调用FROM
项之前,但在这种情况下,它是一个噪音词,因为函数表达式无论如何都可以引用更早的FROM
项。LATERAL
项可以出现在FROM
列表的顶级,也可以出现在JOIN
树中。在后一种情况下,它还可以引用位于JOIN
左侧的任何项,而它位于右侧。当
FROM
项包含LATERAL
交叉引用时,评估过程如下:对于提供交叉引用列的FROM
项的每一行,或提供列的多个FROM
项的行集,LATERAL
项使用该行或行集的列值进行评估。结果行将照常与它们从中计算的行联接。这将对列源表中的每一行或行集重复进行。列源表必须
INNER
或LEFT
连接到LATERAL
项,否则将不会有一组明确定义的行,无法从中计算LATERAL
项的每组行。因此,尽管
这样的构造在语法上有效,但实际上不允许X
RIGHT JOIN LATERALY
Y
引用X
。
WHERE
子句
可选的WHERE
子句具有以下通用形式
WHERE condition
其中*condition
*是任何求值为boolean
类型结果的表达式。任何不满足此条件的行都将从输出中消除。如果用实际行值替换任何变量引用时,行返回 true,则该行满足条件。
GROUP BY
子句
可选的GROUP BY
子句具有以下通用形式
GROUP BY [ ALL | DISTINCT ] grouping_element [, ...]
GROUP BY
将所有对分组表达式具有相同值的选定行浓缩成单行。在*grouping_element
中使用的expression
*可以是输入列名,或输出列(SELECT
列表项)的名称或序号,或由输入列值形成的任意表达式。如果存在歧义,GROUP BY
名称将被解释为输入列名,而不是输出列名。
如果GROUPING SETS
、ROLLUP
或CUBE
中的任何一个作为分组元素出现,则GROUP BY
子句作为一个整体定义了一些独立的*grouping sets
。这相当于在具有各个分组集作为其GROUP BY
子句的子查询之间构造UNION ALL
。可选的DISTINCT
子句在处理前删除重复集;它不*将UNION ALL
转换为UNION DISTINCT
。有关处理分组集的更多详细信息,请参阅第 7.2.4 节。
如果使用了聚合函数,则会对构成每个组的所有行进行计算,为每个组生成一个单独的值。(如果存在聚合函数但没有GROUP BY
子句,则查询将被视为具有包含所有选定行的单个组。)可以通过将FILTER
子句附加到聚合函数调用来进一步筛选馈送到每个聚合函数的行;有关详细信息,请参阅第 4.2.7 节。当存在FILTER
子句时,只有与之匹配的行才包含在该聚合函数的输入中。
当存在GROUP BY
或任何聚合函数时,SELECT
列表表达式引用未分组的列(除非在聚合函数中或未分组的列在功能上依赖于分组的列)是不有效的,因为否则将有多个可能的值可用于未分组的列。如果分组的列(或其子集)是包含未分组列的表的唯一键,则存在函数依赖关系。
请记住,在评估HAVING
子句或SELECT
列表中的任何“标量”表达式之前,会评估所有聚合函数。这意味着,例如,不能使用CASE
表达式来跳过聚合函数的评估;请参见第 4.2.14 节。
当前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和FOR KEY SHARE
不能与GROUP BY
一起指定。
HAVING
子句
可选的HAVING
子句具有以下通用形式
HAVING condition
其中*condition
*与为WHERE
子句指定的相同。
HAVING
消除了不满足条件的组行。HAVING
与WHERE
不同:WHERE
在应用GROUP BY
之前过滤各个行,而HAVING
过滤由GROUP BY
创建的组行。*condition
*中引用的每一列都必须明确引用分组列,除非引用出现在聚合函数中或未分组的列在功能上依赖于分组列。
即使没有GROUP BY
子句,HAVING
的存在也会将查询变成分组查询。这与查询包含聚合函数但没有GROUP BY
子句时发生的情况相同。所有选定的行都被视为形成一个组,并且SELECT
列表和HAVING
子句只能引用聚合函数中的表列。如果HAVING
条件为真,则此类查询将发出单行;如果为假,则发出零行。
当前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和FOR KEY SHARE
不能与HAVING
一起指定。
WINDOW
子句
可选的WINDOW
子句具有以下通用形式
WINDOW window_name AS ( window_definition ) [, ...]
其中*window_name
是可以从OVER
子句或后续窗口定义引用的名称,window_definition
*为
[ existing_window_name ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ frame_clause ]
如果指定了*existing_window_name
*,它必须引用WINDOW
列表中的较早条目;新窗口从该条目复制其分区子句,以及其排序子句(如果存在)。在这种情况下,新窗口不能指定其自己的PARTITION BY
子句,并且只能在复制的窗口没有ORDER BY
时指定ORDER BY
。新窗口始终使用自己的框架子句;复制的窗口不得指定框架子句。
PARTITION BY
列表的元素的解释方式与GROUP BY
子句的元素的解释方式大致相同,不同之处在于它们始终是简单表达式,而不是输出列的名称或编号。另一个不同之处在于,这些表达式可以包含聚合函数调用,而常规GROUP BY
子句中不允许包含聚合函数调用。此处允许包含聚合函数调用,因为窗口化发生在分组和聚合之后。
类似地,ORDER BY
列表的元素的解释方式与语句级ORDER BY
子句的元素的解释方式大致相同,不同之处在于表达式始终被视为简单表达式,而不是输出列的名称或编号。
可选的*frame_clause
为依赖于框架的窗口函数(并非所有函数都依赖于框架)定义窗口框架*。窗口框架是查询的每一行(称为当前行)的一组相关行。*frame_clause
*可以是以下之一
{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion ]
{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ]
其中*frame_start
和frame_end
*可以是以下之一
UNBOUNDED PRECEDING
offset PRECEDING
CURRENT ROW
offset FOLLOWING
UNBOUNDED FOLLOWING
并且*frame_exclusion
*可以是以下之一
EXCLUDE CURRENT ROW
EXCLUDE GROUP
EXCLUDE TIES
EXCLUDE NO OTHERS
如果省略*frame_end
,则其默认为CURRENT ROW
。限制条件是frame_start
不能为UNBOUNDED FOLLOWING
,frame_end
不能为UNBOUNDED PRECEDING
,并且frame_end
选择不能在上述frame_start
和frame_end
选项列表中出现在frame_start
*选择之前,例如,不允许RANGE BETWEEN CURRENT ROW AND*
offset*PRECEDING
。
The default framing option isRANGE UNBOUNDED PRECEDING
, which is the same asRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
; it sets the frame to be all rows from the partition start up through the current row's lastpeer(a row that the window'sORDER BY
clause considers equivalent to the current row; all rows are peers if there is noORDER BY
). In general,UNBOUNDED PRECEDING
means that the frame starts with the first row of the partition, and similarlyUNBOUNDED FOLLOWING
means that the frame ends with the last row of the partition, regardless ofRANGE
,ROWS
orGROUPS
mode. InROWS
mode,CURRENT ROW
means that the frame starts or ends with the current row; but inRANGE
orGROUPS
mode it means that the frame starts or ends with the current row's first or last peer in theORDER BY
ordering. The*offset
PRECEDING
andoffset
FOLLOWING
options vary in meaning depending on the frame mode. InROWS
mode, theoffset
is an integer indicating that the frame starts or ends that many rows before or after the current row. InGROUPS
mode, theoffset
is an integer indicating that the frame starts or ends that many peer groups before or after the current row's peer group, where apeer groupis a group of rows that are equivalent according to the window'sORDER BY
clause. InRANGE
mode, use of anoffset
option requires that there be exactly oneORDER BY
column in the window definition. Then the frame contains those rows whose ordering column value is no more thanoffset
less than (forPRECEDING
) or more than (forFOLLOWING
) the current row's ordering column value. In these cases the data type of theoffset
expression depends on the data type of the ordering column. For numeric ordering columns it is typically of the same type as the ordering column, but for datetime ordering columns it is aninterval
. In all these cases, the value of theoffset
must be non-null and non-negative. Also, while theoffset
*does not have to be a simple constant, it cannot contain variables, aggregate functions, or window functions.
*frame_exclusion
*选项允许将当前行周围的行从框架中排除,即使它们会根据框架开始和框架结束选项包含在内。EXCLUDE CURRENT ROW
将当前行从框架中排除。EXCLUDE GROUP
将当前行及其排序对等项从框架中排除。EXCLUDE TIES
将当前行的任何对等项从框架中排除,但不包括当前行本身。EXCLUDE NO OTHERS
仅明确指定不排除当前行或其对等项的默认行为。
请注意,如果ORDER BY
排序未对行进行唯一排序,则ROWS
模式可能会产生不可预测的结果。RANGE
和GROUPS
模式旨在确保在ORDER BY
排序中作为对等项的行得到相同对待:给定对等组的所有行都将位于框架中或从框架中排除。
一个WINDOW
子句的目的是指定在查询的SELECT
列表或ORDER BY
子句中出现的窗口函数的行为。这些函数可以通过名称在它们的OVER
子句中引用WINDOW
子句条目。但是,不必在任何地方引用WINDOW
子句条目;如果它在查询中未使用,则会简单地忽略它。完全不使用任何WINDOW
子句就可以使用窗口函数,因为窗口函数调用可以在其OVER
子句中直接指定其窗口定义。但是,当多个窗口函数需要相同的窗口定义时,WINDOW
子句可以节省输入。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和FOR KEY SHARE
无法与WINDOW
一起指定。
窗口函数在第 3.5 节、第 4.2.8 节和第 7.2.5 节中进行了详细描述。
SELECT
列表
SELECT
列表(介于关键字SELECT
和FROM
之间)指定构成SELECT
语句的输出行的表达式。这些表达式可以(并且通常会)引用在FROM
子句中计算的列。
就像在表中一样,SELECT
的每个输出列都有一个名称。在简单的SELECT
中,此名称仅用于标记要显示的列,但是当SELECT
是更大查询的子查询时,此名称被更大的查询视为子查询生成的虚拟表的列名称。要指定用于输出列的名称,请在列的表达式后写AS
output_name
。(你可以省略AS
,但仅当所需的输出名称与任何PostgreSQL关键字不匹配时(请参见附录 C)。为了防止将来可能添加关键字,建议始终编写AS
或用双引号引起来输出名称。)如果你不指定列名称,PostgreSQL会自动选择一个名称。如果列的表达式是一个简单的列引用,则所选名称与该列的名称相同。在更复杂的情况下,可以使用函数或类型名称,或者系统可能会使用生成名称,例如?column?
。
输出列的名称可用于在ORDER BY
和GROUP BY
子句中引用列的值,但不能在WHERE
或HAVING
子句中使用;在这些子句中,你必须写出表达式。
在输出列表中,可以使用*
代替表达式,作为所选行的所有列的简写。此外,你可以编写*
table_name*.*
作为仅来自该表的列的简写。在这些情况下,无法使用AS
指定新名称;输出列名称将与表列名称相同。
根据 SQL 标准,应在应用DISTINCT
、ORDER BY
或LIMIT
之前计算输出列表中的表达式。使用DISTINCT
时,这显然是必要的,因为否则不清楚哪些值被设为不同。但是,在许多情况下,如果在ORDER BY
和LIMIT
之后计算输出表达式会很方便;特别是如果输出列表包含任何不稳定的或昂贵的函数。使用这种行为,函数评估的顺序更直观,并且不会有与从未出现在输出中的行相对应的评估。PostgreSQL将在排序和限制之后有效地评估输出表达式,只要这些表达式未在DISTINCT
、ORDER BY
或GROUP BY
中被引用即可。(作为一个反例,SELECT f(x) FROM tab ORDER BY 1
显然必须在排序之前评估f(x)
。)包含集返回函数的输出表达式将在排序之后和限制之前有效地评估,以便LIMIT
将作用于切断来自集返回函数的输出。
注意
9.6 之前的PostgreSQL版本不提供关于输出表达式评估时间与排序和限制之间的任何保证;这取决于所选查询计划的形式。
DISTINCT
子句
如果指定了SELECT DISTINCT
,则会从结果集中删除所有重复行(从每组重复项中保留一行)。SELECT ALL
指定相反的情况:保留所有行;这是默认值。
SELECT DISTINCT ON (*
expression*[, ...] )
仅保留给定表达式评估为相等的每组行的第一行。DISTINCT ON
表达式的解释使用与ORDER BY
相同的规则(见上文)。请注意,除非使用ORDER BY
确保所需行首先出现,否则每组的“第一行”是不可预测的。例如
SELECT DISTINCT ON (location) location, time, report
FROM weather_reports
ORDER BY location, time DESC;
检索每个位置的最新天气报告。但是,如果我们没有使用ORDER BY
来强制每个位置的时间值降序排列,那么我们就会收到每个位置一个不可预测时间段的报告。
DISTINCT ON
表达式必须与最左边的ORDER BY
表达式匹配。ORDER BY
子句通常包含其他表达式,用于确定每个DISTINCT ON
组中行的所需优先级。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和FOR KEY SHARE
无法与DISTINCT
一起指定。
UNION
子句
UNION
子句具有以下通用形式
select_statement UNION [ ALL | DISTINCT ] select_statement
*select_statement
*是任何不带ORDER BY
、LIMIT
、FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
或FOR KEY SHARE
子句的SELECT
语句。(如果ORDER BY
和LIMIT
括在括号中,则可以附加到子表达式。如果没有括号,则这些子句将被视为应用于UNION
的结果,而不是应用于其右侧输入表达式。)
UNION
运算符计算由涉及的SELECT
语句返回的行集并集。如果一行出现在至少一个结果集中,则它位于两个结果集的并集中。表示UNION
直接操作数的两个SELECT
语句必须生成相同数量的列,并且相应列必须具有兼容的数据类型。
除非指定了ALL
选项,否则UNION
的结果不包含任何重复行。ALL
阻止消除重复项。(因此,UNION ALL
通常比UNION
快得多;在可能的情况下使用ALL
。)可以编写DISTINCT
以明确指定消除重复行的默认行为。
除非括号另有指示,否则在同一SELECT
语句中,多个UNION
运算符从左到右求值。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和FOR KEY SHARE
无法为UNION
结果或UNION
的任何输入指定。
INTERSECT
子句
INTERSECT
子句具有以下一般形式
select_statement INTERSECT [ ALL | DISTINCT ] select_statement
*select_statement
*是任何不带ORDER BY
、LIMIT
、FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
或FOR KEY SHARE
子句的SELECT
语句。
INTERSECT
运算符计算涉及的SELECT
语句返回的行集的交集。如果一行出现在两个结果集中,则它位于两个结果集的交集中。
INTERSECT
的结果不包含任何重复行,除非指定了ALL
选项。使用ALL
时,在左表中具有*m
个重复项并在右表中具有n
个重复项的行将在结果集中出现 min(m
,n
*) 次。可以编写DISTINCT
以明确指定消除重复行的默认行为。
同一SELECT
语句中的多个INTERSECT
运算符从左到右求值,除非括号另有规定。INTERSECT
的绑定比UNION
更紧密。也就是说,A UNION B INTERSECT C
将被解读为A UNION (B INTERSECT C)
。
目前,对于INTERSECT
结果或INTERSECT
的任何输入,都无法指定FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和FOR KEY SHARE
。
EXCEPT
子句
EXCEPT
子句具有以下一般形式
select_statement EXCEPT [ ALL | DISTINCT ] select_statement
*select_statement
*是任何不带ORDER BY
、LIMIT
、FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
或FOR KEY SHARE
子句的SELECT
语句。
EXCEPT
运算符计算位于左SELECT
语句结果中但不在右SELECT
语句结果中的行集。
EXCEPT
的结果不包含任何重复行,除非指定了ALL
选项。使用ALL
时,在左表中具有*m
个重复项并在右表中具有n
个重复项的行将在结果集中出现 max(m
-n
*,0) 次。可以编写DISTINCT
以明确指定消除重复行的默认行为。
同一EXCEPT
演算符が同じSELECT
ステートメント内にある場合、括弧で指定しない限り、左から右に評価されます。EXCEPT
はUNION
と同じレベルでバインドします。
現在、FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
、およびFOR KEY SHARE
はEXCEPT
結果またはEXCEPT
の入力には指定できません。
ORDER BY
句
オプションのORDER BY
句にはこの一般的な形式があります
ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...]
ORDER BY
句により、結果行は指定された式に従ってソートされます。2 つの行が最左の式に従って等しい場合、それらは次の式に従って比較されます。指定されたすべての式に従って等しい場合、それらは実装依存の順序で返されます。
各*式
*は出力列の名前または序数 (SELECT
リスト項目) になるか、または入力列値から形成された任意の式になることができます。
序数は出力列の序数 (左から右) の位置を参照します。この機能により、一意の名前を持たない列に基づいて順序を定義できます。AS
句を使用して出力列に名前を割り当てることが常に可能であるため、これは絶対に必要ではありません。
ORDER BY
句では、SELECT
出力リストに表示されない列を含む任意の式を使用することもできます。したがって、次のステートメントは有効です
SELECT name FROM distributors ORDER BY code;
この機能の制限は、UNION
、INTERSECT
、またはEXCEPT
句の結果に適用されるORDER BY
句は、式ではなく出力列名または番号のみを指定できることです。
如果ORDER BY
表达式是一个匹配输出列名称和输入列名称的简单名称,ORDER BY
将会将其解释为输出列名称。这与GROUP BY
在相同情况下做出的选择相反。这种不一致是为了兼容 SQL 标准。
还可以选择在ORDER BY
子句中的任何表达式后添加关键字ASC
(升序)或DESC
(降序)。如果没有指定,则默认情况下假定为ASC
。或者,可以在USING
子句中指定特定的排序运算符名称。排序运算符必须是某个 B 树运算符系列中的小于或大于成员。ASC
通常等效于USING <
,DESC
通常等效于USING >
。(但是,用户定义数据类型的创建者可以确切地定义默认排序顺序,并且它可能对应于具有其他名称的运算符。)
如果指定了NULLS LAST
,则空值在所有非空值之后排序;如果指定了NULLS FIRST
,则空值在所有非空值之前排序。如果两者都没有指定,则当指定或暗示ASC
时,默认行为为NULLS LAST
,当指定DESC
时,默认行为为NULLS FIRST
(因此,默认行为是将空值视为大于非空值)。当指定USING
时,默认空值排序取决于运算符是小于运算符还是大于运算符。
请注意,排序选项仅适用于它们后面的表达式;例如,ORDER BY x, y DESC
与ORDER BY x DESC, y DESC
的含义不同。
字符字符串数据根据适用于要排序的列的排序规则进行排序。可以通过在*expression
*中包含COLLATE
子句来根据需要覆盖该规则,例如ORDER BY mycolumn COLLATE "en_US"
。有关更多信息,请参阅第 4.2.10 节和第 24.2 节。
LIMIT
子句
LIMIT
子句由两个独立的子句组成
LIMIT { count | ALL }
OFFSET start
参数*count
指定要返回的最大行数,而start
指定在开始返回行之前要跳过的行数。当同时指定两者时,在开始计算要返回的count
行之前,将跳过start
*行。
如果*count
表达式求值为 NULL,则将其视为LIMIT ALL
,即没有限制。如果start
*求值为 NULL,则将其视为与OFFSET 0
相同。
SQL:2008 引入了不同的语法来实现相同的结果,PostgreSQL也支持该语法。它就是
OFFSET start { ROW | ROWS }
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }
在此语法中,标准要求*start
或count
值为一个文字常量、一个参数或一个变量名;作为PostgreSQL扩展,允许使用其他表达式,但通常需要用括号括起来以避免歧义。如果在FETCH
子句中省略了count
*,则其默认为 1。WITH TIES
选项用于返回根据ORDER BY
子句在结果集中并列最后一位的任何其他行;在这种情况下,ORDER BY
是强制性的,并且不允许使用SKIP LOCKED
。ROW
和ROWS
以及FIRST
和NEXT
是噪音词,不会影响这些子句的效果。根据标准,如果OFFSET
子句和FETCH
子句同时存在,则OFFSET
子句必须在FETCH
子句之前;但PostgreSQL比较宽松,允许使用任一顺序。
在使用LIMIT
时,最好使用ORDER BY
子句,将结果行约束为一个唯一顺序。否则,你将获得查询行的一个不可预测的子集——你可能要求第十到第二十行,但第十到第二十行的排序是什么?除非你指定ORDER BY
,否则你不知道排序是什么。
查询计划器在生成查询计划时会考虑LIMIT
,因此你很可能会根据对LIMIT
和OFFSET
的使用获得不同的计划(产生不同的行顺序)。因此,使用不同的LIMIT
/OFFSET
值来选择查询结果的不同子集将给出不一致的结果,除非你使用ORDER BY
强制执行可预测的结果排序。这不是一个错误;这是 SQL 不承诺以任何特定顺序提供查询结果这一事实的固有结果,除非使用ORDER BY
来约束顺序。
如果未指定ORDER BY
以强制选择确定性子集,则即使重复执行相同的LIMIT
查询,也可能返回表中行的不同子集。同样,这不是一个错误;在这种情况下,结果的确定性根本无法得到保证。
锁定子句
FOR UPDATE
、FOR NO KEY UPDATE
、FOR SHARE
和FOR KEY SHARE
是锁定子句;它们影响SELECT
在从表中获取行时如何锁定这些行。
锁定子句的一般形式为
FOR lock_strength [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ]
其中*lock_strength
*可以为以下之一
UPDATE
NO KEY UPDATE
SHARE
KEY SHARE
有关每种行级锁定模式的更多信息,请参阅第 13.3.2 节。
要防止操作等待其他事务提交,请使用NOWAIT
或SKIP LOCKED
选项。如果无法立即锁定所选行,则使用NOWAIT
会报告错误,而不是等待。如果使用SKIP LOCKED
,则会跳过无法立即锁定的任何所选行。跳过锁定的行会提供不一致的数据视图,因此这不适用于常规工作,但可用于避免多个使用者访问类似于队列的表时出现锁定争用。请注意,NOWAIT
和SKIP LOCKED
仅适用于行级锁定,而仍然会以普通方式获取所需的ROW SHARE
表级锁定(请参阅第 13 章)。如果需要在不等待的情况下获取表级锁定,则可以先使用LOCK
和NOWAIT
选项。
如果在锁定子句中指定了特定表,则只会锁定来自这些表的行;在SELECT
中使用的任何其他表都会像往常一样读取。不带表列表的锁定子句会影响语句中使用的所有表。如果将锁定子句应用于视图或子查询,则它会影响视图或子查询中使用的所有表。但是,这些子句不适用于主查询引用的WITH
查询。如果希望在WITH
查询中进行行锁定,请在WITH
查询中指定锁定子句。
如果需要为不同的表指定不同的锁定行为,可以编写多个锁定子句。如果同一个表被多个锁定子句提及(或隐式影响),则它将被视为仅由最强的子句指定。类似地,如果在影响它的任何子句中指定了NOWAIT
,则表将被视为NOWAIT
。否则,如果在影响它的任何子句中指定了SKIP LOCKED
,则它将被视为SKIP LOCKED
。
锁定子句不能用于无法将返回的行与各个表行明确标识的上下文中;例如,它们不能与聚合一起使用。
当锁定子句出现在SELECT
查询的顶层时,被锁定的行正是查询返回的行;在连接查询的情况下,被锁定的行是促成返回的连接行的行。此外,满足查询条件的行(截至查询快照)将被锁定,尽管如果它们在快照之后被更新并且不再满足查询条件,它们将不会被返回。如果使用了LIMIT
,则一旦返回了足够的行以满足限制,锁定将停止(但请注意,被OFFSET
跳过的行将被锁定)。类似地,如果在游标的查询中使用了锁定子句,则只有游标实际获取或跨过的行才会被锁定。
当锁定子句出现在子SELECT
中时,被锁定的行是子查询返回给外部查询的行。这可能涉及比单独检查子查询所暗示的更少行,因为外部查询的条件可能用于优化子查询的执行。例如,
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;
将只锁定具有col1 = 5
的行,即使该条件不在子查询的文本中。
以前的版本未能保留由以后保存点升级的锁。例如,此代码
BEGIN;
SELECT * FROM mytable WHERE key = 1 FOR UPDATE;
SAVEPOINT s;
UPDATE mytable SET ... WHERE key = 1;
ROLLBACK TO s;
将在ROLLBACK TO
之后无法保留FOR UPDATE
锁。此问题已在 9.3 版本中修复。
注意
在READ COMMITTED
事务隔离级别下运行并使用ORDER BY
和锁定子句的SELECT
命令可能会返回乱序行。这是因为ORDER BY
首先应用。该命令对结果进行排序,但随后可能会阻塞,尝试获取一行或多行上的锁。一旦SELECT
解除阻塞,一些排序列值可能已被修改,导致这些行看起来乱序(尽管它们按照原始列值排序)。可以通过将FOR UPDATE/SHARE
子句放在子查询中来解决此问题,例如
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1;
请注意,这将导致锁定mytable
的所有行,而顶层的FOR UPDATE
仅锁定实际返回的行。这可能会造成显著的性能差异,特别是如果ORDER BY
与LIMIT
或其他限制结合使用时。因此,仅当预期对排序列进行并发更新且需要严格排序的结果时,才推荐使用此技术。
在REPEATABLE READ
或SERIALIZABLE
事务隔离级别,这将导致序列化失败(SQLSTATE
为'40001'
),因此在这些隔离级别下不可能收到乱序行。
TABLE
命令
命令
TABLE name
等效于
SELECT * FROM name
它可以用作顶级命令或用作复杂查询部分中的节省空间的语法变体。只有WITH
、UNION
、INTERSECT
、EXCEPT
、ORDER BY
、LIMIT
、OFFSET
、FETCH
和FOR
锁定子句可与TABLE
一起使用;WHERE
子句和任何形式的聚合都不能使用。
示例
将表films
与表distributors
连接
SELECT f.title, f.did, d.name, f.date_prod, f.kind
FROM distributors d JOIN films f USING (did);
title | did | name | date_prod | kind
-------------------+-----+--------------+------------+----------
The Third Man | 101 | British Lion | 1949-12-23 | Drama
The African Queen | 101 | British Lion | 1951-08-11 | Romantic
...
对所有影片的列len
求和,并按kind
对结果进行分组
SELECT kind, sum(len) AS total FROM films GROUP BY kind;
kind | total
----------+-------
Action | 07:34
Comedy | 02:58
Drama | 14:28
Musical | 06:42
Romantic | 04:38
对所有影片的列len
求和,按kind
对结果进行分组,并显示小于 5 小时的组总计
SELECT kind, sum(len) AS total
FROM films
GROUP BY kind
HAVING sum(len) < interval '5 hours';
kind | total
----------+-------
Comedy | 02:58
Romantic | 04:38
以下两个示例是根据第二列(name
)的内容对各个结果进行排序的相同方法
SELECT * FROM distributors ORDER BY name;
SELECT * FROM distributors ORDER BY 2;
did | name
-----+------------------
109 | 20th Century Fox
110 | Bavaria Atelier
101 | British Lion
107 | Columbia
102 | Jean Luc Godard
113 | Luso films
104 | Mosfilm
103 | Paramount
106 | Toho
105 | United Artists
111 | Walt Disney
112 | Warner Bros.
108 | Westward
下一个示例展示了如何获取表distributors
和actors
的并集,并将结果限制为每个表中以字母 W 开头的结果。只想要不同的行,因此省略了关键字ALL
。
distributors: actors:
did | name id | name
-----+-------------- ----+----------------
108 | Westward 1 | Woody Allen
111 | Walt Disney 2 | Warren Beatty
112 | Warner Bros. 3 | Walter Matthau
... ...
SELECT distributors.name
FROM distributors
WHERE distributors.name LIKE 'W%'
UNION
SELECT actors.name
FROM actors
WHERE actors.name LIKE 'W%';
name
----------------
Walt Disney
Walter Matthau
Warner Bros.
Warren Beatty
Westward
Woody Allen
此示例展示了如何在FROM
子句中使用函数,无论是否有列定义列表
CREATE FUNCTION distributors(int) RETURNS SETOF distributors AS $$
SELECT * FROM distributors WHERE did = $1;
$$ LANGUAGE SQL;
SELECT * FROM distributors(111);
did | name
-----+-------------
111 | Walt Disney
CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS $$
SELECT * FROM distributors WHERE did = $1;
$$ LANGUAGE SQL;
SELECT * FROM distributors_2(111) AS (f1 int, f2 text);
f1 | f2
-----+-------------
111 | Walt Disney
这是一个带有序数列的函数示例
SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY;
unnest | ordinality
--------+----------
a | 1
b | 2
c | 3
d | 4
e | 5
f | 6
(6 rows)
此示例显示如何使用简单的WITH
子句
WITH t AS (
SELECT random() as x FROM generate_series(1, 3)
)
SELECT * FROM t
UNION ALL
SELECT * FROM t;
x
--------------------
0.534150459803641
0.520092216785997
0.0735620250925422
0.534150459803641
0.520092216785997
0.0735620250925422
请注意,WITH
查询仅评估一次,因此我们得到了两组相同的三组随机值。
此示例使用WITH RECURSIVE
来查找员工 Mary 的所有下属(直接或间接),以及他们的间接级别,从仅显示直接下属的表中
WITH RECURSIVE employee_recursive(distance, employee_name, manager_name) AS (
SELECT 1, employee_name, manager_name
FROM employee
WHERE manager_name = 'Mary'
UNION ALL
SELECT er.distance + 1, e.employee_name, e.manager_name
FROM employee_recursive er, employee e
WHERE er.employee_name = e.manager_name
)
SELECT distance, employee_name FROM employee_recursive;
请注意递归查询的典型形式:初始条件,后跟UNION
,后跟查询的递归部分。确保查询的递归部分最终不会返回任何元组,否则查询将无限循环。(请参阅第 7.8 节了解更多示例。)
此示例使用LATERAL
为manufacturers
表的每一行应用返回集合的函数get_product_names()
SELECT m.name AS mname, pname
FROM manufacturers m, LATERAL get_product_names(m.id) pname;
由于这是内部连接,因此当前没有任何产品的制造商将不会出现在结果中。如果我们希望在结果中包含此类制造商的名称,我们可以执行
SELECT m.name AS mname, pname
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true;
兼容性
当然,SELECT
语句与 SQL 标准兼容。但有一些扩展和一些缺失的功能。
省略的FROM
子句
PostgreSQL允许省略FROM
子句。它有一个直接的用途,即计算简单表达式的结果
SELECT 2+2;
?column?
----------
4
一些其他SQL数据库无法执行此操作,除非引入一个虚拟单行表来执行SELECT
。
空SELECT
列表
在SELECT
之后的输出表达式列表可以为空,生成零列结果表。根据 SQL 标准,这不是有效的语法。PostgreSQL允许这样做,以与允许零列表保持一致。但是,当使用DISTINCT
时,不允许空列表。
省略AS
关键字
在 SQL 标准中,只要新列名是有效的列名(即与任何保留关键字不同),就可以在输出列名之前省略可选关键字AS
。PostgreSQL的限制稍微严格一些:如果新列名与任何关键字(保留或不保留)匹配,则需要AS
。建议的做法是使用AS
或双引号输出列名,以防止与未来添加的关键字发生任何可能的冲突。
在FROM
项中,标准和PostgreSQL都允许在未保留关键字的别名前省略AS
。但这对于输出列名来说是不切实际的,因为存在语法歧义。
在FROM
中省略子SELECT
别名
根据 SQL 标准,FROM
列表中的子SELECT
必须有别名。在PostgreSQL中,可以省略此别名。
ONLY
和继承
SQL 标准要求在编写ONLY
时用括号括住表名,例如SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...
。PostgreSQL认为这些括号是可选的。
PostgreSQL允许编写尾随*
以明确指定包括子表的非ONLY
行为。该标准不允许这样做。
(这些要点同样适用于支持ONLY
选项的所有 SQL 命令。)
TABLESAMPLE
子句限制
目前仅接受常规表和物化视图中的TABLESAMPLE
子句。根据 SQL 标准,应该可以将其应用于任何FROM
项。
FROM
中的函数调用
PostgreSQL允许将函数调用直接写为FROM
列表的成员。在 SQL 标准中,有必要将此类函数调用包装在子SELECT
中;也就是说,语法FROM*
func*(...)*
alias*
大致相当于FROM LATERAL (SELECT*
func*(...))*
alias*
。请注意,LATERAL
被认为是隐式的;这是因为该标准要求LATERAL
语义用于FROM
中的UNNEST()
项。PostgreSQL将UNNEST()
与其他返回集的函数视为相同。
可用于GROUP BY
和ORDER BY
的命名空间
在 SQL-92 标准中,ORDER BY
子句只能使用输出列名或数字,而GROUP BY
子句只能使用基于输入列名的表达式。PostgreSQL扩展了这两个子句,以允许另一个选择(但在存在歧义时使用该标准的解释)。PostgreSQL还允许这两个子句指定任意表达式。请注意,表达式中出现的名称将始终被视为输入列名,而不是输出列名。
SQL:1999 及更高版本使用略有不同的定义,该定义与 SQL-92 并不完全向上兼容。然而,在大多数情况下,PostgreSQL将以 SQL:1999 的方式解释ORDER BY
或GROUP BY
表达式。
函数依赖关系
PostgreSQL仅在表的主键包含在GROUP BY
列表中时才识别函数依赖关系(允许从GROUP BY
中省略列)。SQL 标准指定应识别的其他条件。
LIMIT
和OFFSET
LIMIT
和OFFSET
子句是PostgreSQL特有的语法,MySQL也使用该语法。SQL:2008 标准引入了子句OFFSET ... FETCH {FIRST|NEXT} ...
,用于实现相同的功能,如上文LIMIT 子句中所示。此语法也由IBM DB2使用。(为Oracle编写的应用程序经常使用涉及自动生成的rownum
列的解决方法,该列在 PostgreSQL 中不可用,以实现这些子句的效果。)
FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
、FOR KEY SHARE
虽然FOR UPDATE
出现在 SQL 标准中,但该标准仅允许将其作为DECLARE CURSOR
的一个选项。PostgreSQL允许在任何SELECT
查询以及子SELECT
中使用它,但这是一种扩展。变体FOR NO KEY UPDATE
、FOR SHARE
和FOR KEY SHARE
,以及选项NOWAIT
和SKIP LOCKED
,未出现在该标准中。
在WITH
中的数据修改语句
PostgreSQL允许将INSERT
、UPDATE
和DELETE
用作WITH
查询。这在 SQL 标准中找不到。
非标准子句
DISTINCT ON ( ... )
是 SQL 标准的扩展。
ROWS FROM( ... )
是 SQL 标准的扩展。
WITH
的MATERIALIZED
和NOT MATERIALIZED
选项是 SQL 标准的扩展。