SELECT, TABLE, WITH — 从表或视图检索行
[ WITH [ RECURSIVE ]with_query
[, ...] ] SELECT [ ALL | DISTINCT [ ON (expression
[, ...] ) ] ] [ { * |expression
[ [ AS ]output_name
] } [, ...] ] [ FROMfrom_item
[, ...] ] [ WHEREcondition
] [ GROUP BY [ ALL | DISTINCT ]grouping_element
[, ...] ] [ HAVINGcondition
] [ WINDOWwindow_name
AS (window_definition
) [, ...] ] [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ]select
] [ ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...] ] [ LIMIT {count
| ALL } ] [ OFFSETstart
[ ROW | ROWS ] ] [ FETCH { FIRST | NEXT } [count
] { ROW | ROWS } { ONLY | WITH TIES } ] [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OFfrom_reference
[, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ] wherefrom_item
can be one of: [ ONLY ]table_name
[ * ] [ [ AS ]alias
[ (column_alias
[, ...] ) ] ] [ TABLESAMPLEsampling_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
{ ONjoin_condition
| USING (join_column
[, ...] ) [ ASjoin_using_alias
] }from_item
NATURALjoin_type
from_item
from_item
CROSS JOINfrom_item
andgrouping_element
can be one of: ( )expression
(expression
[, ...] ) ROLLUP ( {expression
| (expression
[, ...] ) } [, ...] ) CUBE ( {expression
| (expression
[, ...] ) } [, ...] ) GROUPING SETS (grouping_element
[, ...] ) andwith_query
is:with_query_name
[ (column_name
[, ...] ) ] AS [ [ NOT ] MATERIALIZED ] (select
|values
|insert
|update
|delete
|merge
) [ SEARCH { BREADTH | DEPTH } FIRST BYcolumn_name
[, ...] SETsearch_seq_col_name
] [ CYCLEcolumn_name
[, ...] SETcycle_mark_col_name
[ TOcycle_mark_value
DEFAULTcycle_mark_default
] USINGcycle_path_col_name
] TABLE [ ONLY ]table_name
[ * ]
SELECT
从零个或多个表中检索行。SELECT
的一般处理过程如下:
计算 WITH
列表中的所有查询。这些查询有效地充当可在 FROM
列表中引用的临时表。WITH
查询在 FROM
中被多次引用时,只计算一次,除非使用 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
以显式指定删除重复行。请注意,DISTINCT
在这里是默认行为,即使 ALL
是 SELECT
本身的默认行为。(请参阅下面的 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
或 MERGE
语句。当在 WITH
中编写数据修改语句(INSERT
、UPDATE
、DELETE
或 MERGE
)时,通常包含 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
查询,则会被忽略。
默认情况下,如果无副作用的 WITH
查询在主查询的 FROM
子句中仅使用一次,则它会被折叠到主查询中。这允许在语义上不可见的情况下对两个查询级别进行联合优化。但是,可以通过将 WITH
查询标记为 MATERIALIZED
来防止这种折叠。例如,如果 WITH
查询被用作优化屏障以防止规划器选择错误的计划,则这可能很有用。v12 之前的 PostgreSQL 版本从未进行过此类折叠,因此为旧版本编写的查询可能依赖 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
采样方法各自接受一个 argument
,它是要采样的表的比例,表示为 0 到 100 之间的百分比。此参数可以是任何 real
值表达式。(其他采样方法可能会接受更多或不同的参数。)这两种方法各自返回一个随机选择的表样本,该样本将包含表中大约指定百分比的行。BERNOULLI
方法扫描整个表,并使用指定的概率独立地选择或忽略单个行。SYSTEM
方法执行块级采样,每个块具有被选中的指定机会;将返回每个选定块中的所有行。当指定小的采样百分比时,SYSTEM
方法比 BERNOULLI
方法快得多,但由于聚类效应,它可能会返回不太随机的表样本。
可选的 REPEATABLE
子句指定一个 seed
数字或表达式,用于在采样方法中生成随机数。种子值可以是任何非空浮点值。如果该表在此期间未更改,则两个指定相同种子和 argument
值的查询将选择表的相同样本。但是,不同的种子值通常会产生不同的样本。如果未给出 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
返回所有连接的行,再加上每个不匹配的右侧行的行(在左侧扩展为 null)。这只是一种符号上的方便,因为您可以通过切换左右表将其转换为 LEFT OUTER JOIN
。
FULL OUTER JOIN
返回所有连接的行,再加上每个不匹配的左侧行的行(在右侧扩展为 null),再加上每个不匹配的右侧行的行(在左侧扩展为 null)。
ON join_condition
join_condition
是一个表达式,其结果为 boolean
类型(类似于 WHERE
子句),用于指定连接中哪些行被认为匹配。
USING ( join_column
[, ...] ) [ AS join_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
项。结果行照常与计算它们的行连接。对于来自列源表的每一行或行集,都会重复此操作。
列源表必须与 LATERAL
项进行 INNER
或 LEFT
连接,否则将没有明确定义的行集来从中计算 LATERAL
项的每一组行。因此,虽然诸如
之类的构造在语法上是有效的,但实际上不允许 X
RIGHT JOIN LATERAL Y
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
子句定义一些独立的 分组集
。此效果等效于在子查询之间构造 UNION ALL
,这些子查询将单独的分组集作为其 GROUP BY
子句。可选的 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
子句具有以下一般形式
WINDOWwindow_name
AS (window_definition
) [, ...]
其中 window_name
是一个名称,可以从 OVER
子句或后续窗口定义中引用,并且 window_definition
是
[existing_window_name
] [ PARTITION BYexpression
[, ...] ] [ ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ 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 } BETWEENframe_start
ANDframe_end
[frame_exclusion
]
其中 frame_start
和 frame_end
可以是以下之一
UNBOUNDED PRECEDINGoffset
PRECEDING CURRENT ROWoffset
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
默认的框架选项是 RANGE UNBOUNDED PRECEDING
,它与 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
相同;它将框架设置为从分区开始到当前行的最后一个对等行的所有行(窗口的 ORDER BY
子句认为该行与当前行等效;如果没有 ORDER BY
,则所有行都是对等行)。通常,UNBOUNDED PRECEDING
表示框架从分区的首行开始,类似地,UNBOUNDED FOLLOWING
表示框架以分区的末行结束,而与 RANGE
、ROWS
或 GROUPS
模式无关。在 ROWS
模式下,CURRENT ROW
表示框架以当前行开始或结束;但在 RANGE
或 GROUPS
模式下,它表示框架以 ORDER BY
排序中当前行的第一个或最后一个对等行开始或结束。offset
PRECEDING
和 offset
FOLLOWING
选项的含义因框架模式而异。在 ROWS
模式下,offset
是一个整数,表示框架在当前行之前或之后开始或结束的行数。在 GROUPS
模式下,offset
是一个整数,表示框架在当前行的对等组之前或之后开始或结束的对等组数,其中对等组是根据窗口的 ORDER BY
子句等效的一组行。在 RANGE
模式下,使用 offset
选项需要窗口定义中恰好有一个 ORDER BY
列。然后,框架包含那些排序列值不超过当前行的排序列值小于(对于 PRECEDING
)或大于(对于 FOLLOWING
)offset
的行。在这些情况下,offset
表达式的数据类型取决于排序列的数据类型。对于数字排序列,它通常与排序列的类型相同,但对于日期时间排序列,它是一个 interval
。在所有这些情况下,offset
的值必须为非空且非负。此外,虽然 offset
不必是简单的常量,但它不能包含变量、聚合函数或窗口函数。
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
一起指定。
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
将作用于截断来自集合返回函数的输出。
PostgreSQL 9.6 之前的版本不保证输出表达式的求值时间与排序和限制之间的关系;这取决于所选查询计划的形式。
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
语句必须产生相同数量的列,并且对应的列必须具有兼容的数据类型。
UNION
的结果不包含任何重复的行,除非指定了 ALL
选项。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)
。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
不能为 INTERSECT
结果或任何 INTERSECT
的输入指定。
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
语句的结果中存在但不在右侧结果中的行的集合。
EXCEPT
的结果不包含任何重复的行,除非指定了 ALL
选项。使用 ALL
,在左表中具有 m
个重复项且在右表中具有 n
个重复项的行将在结果集中出现 max(m
-n
,0) 次。可以编写 DISTINCT
来明确指定删除重复行的默认行为。
同一个 SELECT
语句中的多个 EXCEPT
运算符从左到右求值,除非括号另有规定。EXCEPT
的绑定级别与 UNION
相同。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
不能为 EXCEPT
结果或任何 EXCEPT
的输入指定。
ORDER BY
子句可选的 ORDER BY
子句具有以下一般形式
ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...]
ORDER BY
子句导致结果行按照指定的表达式进行排序。如果根据最左边的表达式两行相等,则根据下一个表达式进行比较,依此类推。如果根据所有指定的表达式它们都相等,则它们将以依赖于实现的方式返回。
每个 expression
可以是输出列(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
的含义不同。
字符型字符串数据根据排序的列所应用的排序规则进行排序。 可以根据需要在表达式
中包含 COLLATE
子句来覆盖此规则,例如 ORDER BY mycolumn COLLATE "en_US"
。 更多信息请参见第 4.2.10 节和第 23.2 节。
LIMIT
子句LIMIT
子句由两个独立的子子句组成
LIMIT {count
| ALL } OFFSETstart
参数 count
指定要返回的最大行数,而 start
指定在开始返回行之前要跳过的行数。 当两者都被指定时,会先跳过 start
行,然后再开始计算要返回的 count
行。
如果 count
表达式的计算结果为 NULL,则将其视为 LIMIT ALL
,即没有限制。 如果 start
的计算结果为 NULL,则将其视为与 OFFSET 0
相同。
SQL:2008 引入了不同的语法来实现相同的结果,PostgreSQL 也支持此语法。 它如下所示
OFFSETstart
{ 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
在从表中获取行时如何锁定行。
锁定子句的一般形式为
FORlock_strength
[ OFfrom_reference
[, ...] ] [ NOWAIT | SKIP LOCKED ]
其中 lock_strength
可以是以下之一
UPDATE NO KEY UPDATE SHARE KEY SHARE
from_reference
必须是在 FROM
子句中引用的表 别名
或非隐藏的 table_name
。 有关每个行级锁定模式的更多信息,请参阅第 13.3.2 节。
为了防止操作等待其他事务提交,请使用 NOWAIT
或 SKIP LOCKED
选项。 使用 NOWAIT
,如果无法立即锁定选定的行,则该语句会报告错误,而不是等待。 使用 SKIP LOCKED
,会跳过任何无法立即锁定的选定行。 跳过锁定的行会提供不一致的数据视图,因此这不适用于通用工作,但可以用来避免多个使用者访问类似队列的表时的锁争用。 请注意,NOWAIT
和 SKIP LOCKED
仅适用于行级锁——所需的 ROW SHARE
表级锁仍以通常的方式获取(请参阅第 13 章)。 如果您需要先获取表级锁而不等待,您可以首先使用带有 NOWAIT
选项的 LOCK
。
如果在锁定子句中指定了特定表,则只会锁定来自这些表的行; 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
将集合返回函数 get_product_names()
应用于 manufacturers
表的每一行。
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
被认为是隐式的;这是因为标准要求 FROM
中的 UNNEST()
项具有 LATERAL
语义。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
表达式。
仅当表的 主键 包含在 GROUP BY
列表中时,PostgreSQL 才会识别函数依赖(允许从 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
和 MERGE
用作 WITH
查询。这在 SQL 标准中找不到。
DISTINCT ON ( ... )
是 SQL 标准的扩展。
ROWS FROM( ... )
是 SQL 标准的扩展。
WITH
的 MATERIALIZED
和 NOT MATERIALIZED
选项是 SQL 标准的扩展。
如果您在文档中发现任何不正确、与您使用特定功能的体验不符或需要进一步澄清的地方,请使用此表单报告文档问题。