pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
SELECT, TABLE, WITH — retrieve rows from a table or view
[ WITH [ RECURSIVE ]with_query[, ...] ] SELECT [ ALL | DISTINCT [ ON (expression[, ...] ) ] ] * |expression[ [ AS ]output_name] [, ...] [ FROMfrom_item[, ...] ] [ WHEREcondition] [ GROUP BYexpression[, ...] ] [ HAVINGcondition[, ...] ] [ WINDOWwindow_nameAS (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 ] [ FOR { UPDATE | SHARE } [ OFtable_name[, ...] ] [ NOWAIT ] [...] ] wherefrom_itemcan be one of: [ ONLY ]table_name[ * ] [ [ AS ]alias[ (column_alias[, ...] ) ] ] (select) [ AS ]alias[ (column_alias[, ...] ) ]with_query_name[ [ AS ]alias[ (column_alias[, ...] ) ] ]function_name( [argument[, ...] ] ) [ AS ]alias[ (column_alias[, ...] |column_definition[, ...] ) ]function_name( [argument[, ...] ] ) AS (column_definition[, ...] )from_item[ NATURAL ]join_typefrom_item[ ONjoin_condition| USING (join_column[, ...] ) ] andwith_queryis:with_query_name[ (column_name[, ...] ) ] AS (select|insert|update|delete) TABLE [ ONLY ]table_name[ * ]
SELECT 从零个或多个表中检索行。SELECT 的一般处理流程如下:
WITH列表中的所有查询都会被计算。这些实际上充当临时表,可以在FROM列表中引用。在FROM列表中多次引用的WITH查询只会计算一次。(参见下面的WITH Clause。)
所有FROM列表中的元素都会被计算。(FROM列表中的每个元素都是一个真实或虚拟表。)如果在FROM列表中指定了多个元素,则它们会被交叉连接在一起。(参见下面的FROM Clause。)
如果指定了WHERE子句,则不满足条件的所有行将从输出中删除。(请参见下面的WHERE Clause。)
如果指定了GROUP BY子句,或者存在聚合函数调用,输出将被组合成在一个或多个值上匹配的行组,并计算聚合函数的结果。如果存在HAVING子句,它将消除不满足给定条件的组。(参见GROUP BY Clause和HAVING Clause。)
实际输出行是使用每个选定行或行组的SELECT输出表达式计算的。(参见下面的SELECT List。)
SELECT DISTINCT消除结果中的重复行。SELECT DISTINCT ON会消除在所有指定表达式上匹配的行,只保留每组中的第一行。SELECT ALL(默认)将返回所有候选行,包括重复行。(参见下面的DISTINCT Clause。)
使用操作符UNION、INTERSECT和EXCEPT,可以将多个SELECT语句的输出合并成一个结果集。UNION操作符返回在一个或两个结果集中的所有行。INTERSECT操作符返回同时出现在两个结果集中的所有行。EXCEPT操作符返回在第一个结果集中但不在第二个结果集中的行。在这三种情况下,除非指定ALL,否则将消除重复行。还可以添加噪声词DISTINCT,以明确指定去重。请注意,这里的默认行为是DISTINCT,即使SELECT本身的默认行为是ALL。(请参见下面的UNION Clause、INTERSECT Clause和EXCEPT Clause。)
如果指定了ORDER BY子句,则返回的行按指定顺序排序。如果没有给出ORDER BY,则按系统认为最快的顺序返回行。(参见下面的ORDER BY Clause。)
如果指定了LIMIT(或FETCH FIRST)或OFFSET子句,SELECT语句只返回结果行的子集。(参见下面的LIMIT Clause。)
如果指定了 FOR UPDATE 或 FOR SHARE,SELECT 语句将选定的行锁定,防止并发更新(参见下面的FOR UPDATE/FOR SHARE Clause)。
你必须拥有 SELECT 命令中使用到的每一列上的 SELECT 权限。使用 FOR UPDATE 或 FOR SHARE 还要求 UPDATE 权限(对于每个这样选中的表,至少要有一列具有该权限)。
WITH ClauseWITH 子句允许你指定一个或多个可在主查询中按名称引用的子查询。这些子查询在主查询执行期间实际上充当临时表或视图。每个子查询都可以是一条 SELECT、INSERT、UPDATE 或 DELETE 语句。在 WITH 中写数据修改语句(INSERT、UPDATE 或 DELETE)时,通常会带上 RETURNING 子句。构成主查询所读取临时表的,是 RETURNING 的输出,而不是语句所修改的底层表。如果省略 RETURNING,语句仍会执行,但不产生任何输出,因此主查询无法把它当作表来引用。
对于每个WITH查询,都必须指定一个名称(不带模式限定)。还可以指定一个列名列表;如果省略,则列名将从子查询中推导出来。
如果指定了RECURSIVE,则允许一个 SELECT子查询使用名称引用自身。这样一个子查询的形式必须是
non_recursive_termUNION [ ALL | DISTINCT ]recursive_term
其中递归自引用必须出现在UNION的右手边。每个查询中只允许一个递归自引用。不支持递归数据修改语句,但是可以在一个数据查询语句中使用一个递归 SELECT查询的结果。示例可见第 7.8 节。
RECURSIVE的另一个效果是 WITH查询不需要被排序:一个查询可以引用另一个在列表中比它靠后的查询(不过,循环引用或者互递归没有实现)。如果没有RECURSIVE,WITH 查询只能引用在WITH列表中位置更前面的兄弟 WITH查询。
WITH查询的一个关键特性是,每次执行主查询时,它们都只会求值一次,即使主查询多次引用它们。特别是,无论主查询是否读取了它们的全部输出或任何输出,数据修改语句都保证执行一次且仅执行一次。
主查询和WITH查询(概念上)都在同一时间执行。这意味着,除了读取其RETURNING输出之外,查询的其他部分都看不到WITH中数据修改语句的效果。如果两个这样的数据修改语句试图修改同一行,结果未指定。
更多信息请见第 7.8 节。
FROM ClauseFROM 子句为 SELECT 指定一个或更多源表。如果指定了多个源表,结果将是所有源表的笛卡尔积(交叉连接)。但通常会增加限定条件,把返回的行限制为该笛卡尔积的一个小子集。
该 FROM 子句可以包含以下元素:
table_name要扫描的现有表或视图的名称(可选模式限定符)。如果在表名之前指定ONLY,则仅扫描该表。如果未指定ONLY,则扫描该表及其所有后代表(如果有)。可选地,可以在表名后指定*,以明确指示包括后代表。
alias包含该别名的FROM项的替代名称。别名可用于简写,或者消除自连接(同一张表被扫描多次)中的歧义。提供别名后,它会完全隐藏表或函数的实际名称;例如给定FROM foo AS f,SELECT的其余部分必须把这个FROM 项写成f而不是foo。如果写了别名,还可以写列别名列表,为该表的一个或多个列提供替代名称。
select子SELECT可以出现在FROM子句中,它的作用就像在这个SELECT命令的执行期间创建了一个临时表。注意,子SELECT必须用圆括号括起来,并且必须为其提供别名。这里也可以使用VALUES命令。
with_query_nameWITH查询通过写出它的名称来引用,就像该查询名是表名一样。(事实上,对于主查询而言,WITH查询会遮蔽任何同名的真实表;如有必要,可以通过模式限定表名来引用该同名真实表。)也可以像对待表一样为它提供别名。
function_name函数调用可以出现在FROM子句中。(这对于返回结果集的函数尤其有用,但任何函数都可以使用。)其效果就像在这条SELECT命令执行期间,将函数的输出创建成了一张临时表。也可以使用别名。如果写了别名,还可以写一个列别名列表,为函数复合返回类型中的一个或多个属性提供替代名称。如果函数被定义为返回record数据类型,则必须给出别名或关键字AS,后面跟一个如下形式的列定义列表:( 。列定义列表必须与函数实际返回的列数和类型相匹配。column_name data_type [, ... ] )
join_type以下之一:
[ INNER ] JOIN
LEFT [ OUTER ] JOIN
RIGHT [ OUTER ] JOIN
FULL [ OUTER ] JOIN
CROSS JOIN
For the INNER and OUTER join types, a join condition must be specified, namely exactly one of NATURAL, ON , or join_conditionUSING (. See below for the meaning. For join_column [, ...])CROSS JOIN, none of these clauses can appear.
JOIN 子句组合两个 FROM 项。如有必要可用圆括号确定嵌套的顺序。在没有圆括号时,JOIN 从左到右嵌套。在任何情况下,JOIN 的结合都比分隔 FROM 项的逗号更紧。
CROSS JOIN和INNER JOIN产生简单的笛卡尔积,与在FROM顶层列出这两个表得到的结果相同,但会受到连接条件(如果有)的限制。CROSS JOIN等价于INNER JOIN ON (TRUE),也就是说,没有行会被条件过滤掉。这些连接类型只是提供了一种方便的记法,因为它们所做的一切都可以用普通的FROM和WHERE完成。
LEFT OUTER JOIN返回已限定笛卡尔积中的所有行(即,通过其连接条件的所有组合行),外加左侧表中每一行的一个副本;对于这些行,不存在通过连接条件的右侧行。这样的左侧行会通过在右侧列中插入空值而扩展到连接表的完整宽度。注意,在决定哪些行有匹配项时,只考虑JOIN子句自身的条件;外层条件是在之后应用的。
相反,RIGHT OUTER JOIN返回所有连接后的行,再加上每个未匹配右侧行对应的一行(左侧用空值扩展)。这只是一种记法上的便利,因为你可以通过交换左右表将其改写成LEFT OUTER JOIN。
FULL OUTER JOIN返回所有连接后的行,再加上每个未匹配的左侧行(右侧用空值扩展),以及每个未匹配的右侧行(左侧用空值扩展)。
ON join_conditionjoin_condition 是一个表达式,其结果类型为 boolean(类似于 WHERE 子句),用于指定连接中哪些行被视为匹配。
USING ( join_column [, ...] )一个形如USING ( a, b, ... )的子句是ON left_table.a = right_table.a AND left_table.b = right_table.b ...的简写。此外,USING意味着只有每对等价列中的一个会包含在连接输出中,而不是两者都包含。
NATURALNATURAL 是一个简写,表示一个列出两个表中所有同名列的 USING 列表。
WHERE Clause可选的WHERE子句的形式
WHERE condition
其中condition 是任一计算得到boolean类型结果的表达式。任何不满足这个条件的行都会从输出中被消除。如果用一行的实际值替换其中的变量引用后,该表达式返回真,则该行符合条件。
GROUP BY Clause可选的 GROUP BY 子句的一般形式为
GROUP BY expression [, ...]
GROUP BY会把所有在分组表达式上具有相同值的已选中行压缩成单独一行。expression可以是输入列名、输出列(SELECT列表项)的名称或序号或者由输入列值构成的任意表达式。在出现歧义时,GROUP BY名称将被解释为输入列名而不是输出列名。
(相反,如果没有GROUP BY,聚集会对所有被选中的行计算产生单个值。)当存在GROUP BY子句时,SELECT列表表达式不能引用非分组列,除非是在聚合函数内部,或者该非分组列函数依赖于分组列,因为否则对于一个非分组列可能会有多个可返回的值。如果分组列(或其子集)是包含该非分组列的表的主键,则存在函数依赖。
HAVING Clause可选的HAVING子句的形式
HAVING condition
其中condition与 WHERE子句中指定的条件相同。
HAVING消除不满足该条件的分组行。HAVING与WHERE不同:WHERE会在应用GROUP BY之前过滤个体行,而HAVING过滤由 GROUP BY创建的分组行。condition中引用的每一列都必须无歧义地引用某个分组列,除非该引用出现在聚合函数中,或者该非分组列函数依赖于分组列。
即使没有GROUP BY子句,HAVING 的存在也会把一个查询转变成一个分组查询。这和查询中包含聚合函数但没有 GROUP BY子句时的情况相同。所有被选择的行都被认为是一个单一分组,并且SELECT列表和 HAVING子句只能从聚合函数内部引用表列。如果该 HAVING条件为真,这样的查询将输出单独一行;否则不返回行。
WINDOW Clause可选的 WINDOW 子句的一般形式为:
WINDOWwindow_nameAS (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。新窗口总是使用自己的帧子句,而被复制的窗口不得指定帧子句。
PARTITION BY列表元素的解释以GROUP BY Clause元素的方式进行,不过它们总是简单表达式并且绝不能是输出列的名称或编号。另一个区别是这些表达式可以包含聚合函数调用,而这在常规GROUP BY 子句中是不被允许的。它们被允许的原因是窗口是出现在分组和聚合之后的。
类似地,ORDER BY列表元素的解释也以语句级ORDER BY Clause元素的方式进行,不过该表达式总是被当做简单表达式并且绝不会是输出列的名称或编号。
可选的 frame_clause 定义窗口帧,供依赖于帧的窗口函数使用(并非所有窗口函数都依赖帧)。窗口帧是与查询中每一行相关联的一组行;查询中的这一行称为当前行。frame_clause 可以是以下之一:
{ RANGE | ROWS } frame_start
{ RANGE | ROWS } BETWEEN frame_start AND frame_end
其中,frame_start 和 frame_end 可以是以下之一:
UNBOUNDED PRECEDINGvaluePRECEDING CURRENT ROWvalueFOLLOWING UNBOUNDED FOLLOWING
如果省略 frame_end,则默认为 CURRENT ROW。其限制是:frame_start 不能是 UNBOUNDED FOLLOWING,frame_end 不能是 UNBOUNDED PRECEDING,并且 frame_end 在上面列表中的位置不能早于 frame_start 的位置 — 例如,RANGE BETWEEN CURRENT ROW AND 是不允许的。value PRECEDING
默认的帧选项是RANGE UNBOUNDED PRECEDING,它等同于RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;它将帧设置为从分区起点到当前行最后一个同等行的所有行(同等行是ORDER BY认为与当前行等价的行;如果没有ORDER BY,则所有行都属于同等行)。一般而言,UNBOUNDED PRECEDING表示帧从分区的第一行开始,而UNBOUNDED FOLLOWING同样表示帧在分区的最后一行结束(无论采用RANGE还是ROWS模式)。在ROWS模式下,CURRENT ROW表示帧从当前行开始或在当前行结束;但在RANGE模式下,它表示帧从ORDER BY排序中当前行的第一个同等行开始,或在最后一个同等行结束。目前,value PRECEDING和value FOLLOWING仅允许用于ROWS模式。它们表示帧从当前行之前或之后相应行数的那一行开始或结束。value必须是不包含任何变量、聚合函数或窗口函数的整数表达式。该值不能为空值或负数,但可以为零,此时选择当前行本身。
请注意,如果ORDER BY排序没有将行排成唯一顺序,ROWS选项可能产生不可预测的结果。RANGE选项旨在确保ORDER BY排序中的同等行得到相同处理;所有同等行都会位于同一帧中。
WINDOW子句的目的是指定出现在查询的SELECT List或ORDER BY Clause中的窗口函数的行为。这些函数可以在它们的 OVER子句中用名称引用WINDOW 子句项。不过,WINDOW子句项不是必须被引用。如果在查询中没有用到它,它会被简单地忽略。可以使用根本没有任何 WINDOW子句的窗口函数,因为窗口函数调用可以直接在其OVER子句中指定它的窗口定义。不过,当多个窗口函数都需要相同的窗口定义时,WINDOW子句能够减少输入量。
SELECT ListSELECT列表(位于关键词 SELECT和FROM之间)指定构成 SELECT语句输出行的表达式。这些表达式可以(并且通常确实会)引用FROM子句中计算得到的列。
正如在表中一样,SELECT 的每一个输出列都有一个名称。在一个简单的 SELECT 中,这个名称只是被用来标记要显示的列,但是当 SELECT 是一个大型查询的子查询时,大型查询会把该名称看做子查询产生的虚表的列名。要指定输出列使用的名称,可在列的表达式后面写上 AS output_name。(可以省略 AS,但仅当期望的输出名不与任何 PostgreSQL 关键字(见附录 C)冲突时。为了防止将来可能新增关键字,建议始终写上 AS,或者给输出名加上双引号。)如果没有指定列名,名称将由 PostgreSQL 自动选择:如果列的表达式是一个简单的列引用,所选名称就是该列的名称;在更复杂的情况下,通常会使用形如 ?column 的生成名称。N?
一个输出列的名称可以被用来在ORDER BY以及 GROUP BY子句中引用该列的值,但是不能用于 WHERE和HAVING子句(在其中必须写出表达式)。
可以在输出列表中写*来取代表达式,它是被选中行的所有列的一种简写方式。还可以写 ,它是只来自那个表的所有列的简写形式。在这些情况中无法用 table_name.*AS指定新的名称,输出行的名称将和表列的名称相同。
DISTINCT Clause如果指定了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分组内行的优先级。
UNION ClauseUNION 子句的一般形式如下:
select_statementUNION [ ALL | DISTINCT ]select_statement
select_statement 是任何不带 ORDER BY、LIMIT、FOR UPDATE 或 FOR SHARE 子句的 SELECT 语句。(如果用圆括号括起,ORDER BY 和 LIMIT 可以附着到子表达式上。如果没有圆括号,这些子句将被认为作用于 UNION 的结果,而不是其右侧的输入表达式。)
UNION操作符计算相关 SELECT语句所返回的行的并集。如果一行至少出现在两个结果集中的一个内,它就会在并集中。作为 UNION两个操作数的 SELECT语句必须产生相同数量的列并且对应位置上的列必须具有兼容的数据类型。
UNION的结果不会包含重复行,除非指定了 ALL选项。ALL会阻止消除重复(因此,UNION ALL通常显著地快于UNION,尽量使用ALL)。也可以写上DISTINCT,以显式指定默认的去重行为。
除非用圆括号指定计算顺序,同一个SELECT语句中的多个 UNION操作符会从左至右计算。
当前,FOR UPDATE 和 FOR SHARE 不能为 UNION 的结果或 UNION 的任何输入指定。
INTERSECT ClauseINTERSECT 子句的一般形式如下:
select_statementINTERSECT [ ALL | DISTINCT ]select_statement
select_statement is any SELECT statement without an ORDER BY, LIMIT, FOR UPDATE, or FOR SHARE clause.
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 UPDATE 和 FOR SHARE 不能为 INTERSECT 的结果或 INTERSECT 的任何输入指定。
EXCEPT ClauseEXCEPT 子句的一般形式如下:
select_statementEXCEPT [ ALL | DISTINCT ]select_statement
select_statement is any SELECT statement without an ORDER BY, LIMIT, FOR UPDATE, or FOR SHARE clause.
EXCEPT操作符计算位于左侧 SELECT语句的结果中但不在右侧语句结果中的行集合。
EXCEPT的结果不会包含重复行,除非指定了 ALL选项。如果有ALL,一个在左表中有 m次重复并且在右表中有 n次重复的行将会在结果集中出现 max(m-n,0) 次。也可以写上DISTINCT,以显式指定默认的去重行为。
除非用圆括号指定计算顺序,同一个SELECT语句中的多个 EXCEPT操作符会从左至右计算。EXCEPT的优先级与 UNION相同。
当前,FOR UPDATE 和 FOR SHARE 不能为 EXCEPT 的结果或 EXCEPT 的任何输入指定。
ORDER BY Clause可选的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是不同的。
字符串数据会按适用于被排序列的排序规则来排序。根据需要可以通过在 expression中包括一个 COLLATE子句来覆盖,例如 ORDER BY mycolumn COLLATE "en_US"。更多信息请见第 4.2.10 节和第 22.2 节。
LIMIT Clause该 LIMIT 子句由两个独立的子句组成:
LIMIT { count | ALL }
OFFSET start
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
在这种语法中,要为 start 或 count 写除简单整数常量以外的任何内容,必须给它加上圆括号。如果在 FETCH 子句中省略 count,它默认为 1。ROW、ROWS 以及 FIRST、NEXT 都是不影响这些子句效果的赘词。按照标准,如果两个子句同时出现,OFFSET 子句必须在 FETCH 子句之前;但 PostgreSQL 更宽松,允许任意顺序。
在使用LIMIT时,用一个ORDER BY子句把结果行约束到一个唯一顺序是个好办法。否则你将得到该查询结果行的一个不可预测的子集 — 你可能要求从第 10 到第 20 行,但是在什么顺序下的第 10 到第 20 呢?除非指定ORDER BY,你是不知道顺序的。
查询规划器在生成一个查询计划时会考虑LIMIT,因此根据你使用的LIMIT和OFFSET,你很可能得到不同的计划(得到不同的行序)。所以,使用不同的 LIMIT/OFFSET值来选择一个查询结果的不同子集将会给出不一致的结果,除非你用ORDER BY强制一种可预测的结果顺序。这不是一个缺陷,它是 SQL 不承诺以任何特定顺序(除非使用 ORDER BY来约束顺序)给出一个查询结果这一事实造成的必然后果。
如果没有一个ORDER BY来强制选择一个确定的子集,重复执行同样的LIMIT查询甚至可能会返回一个表中行的不同子集。同样,这也不是一种缺陷,在这种情况下也无法保证结果的确定性。
FOR UPDATE/FOR SHARE ClauseFOR UPDATE 子句的形式如下:
FOR UPDATE [ OF table_name [, ...] ] [ NOWAIT ]
与它密切相关的 FOR SHARE 子句的形式如下:
FOR SHARE [ OF table_name [, ...] ] [ NOWAIT ]
FOR UPDATE 导致由 SELECT 语句检索到的行被锁定,就像要更新它们一样。这可以防止它们被其他事务修改或删除,直到当前事务结束。也就是说,其他事务中试图对这些行执行 UPDATE、DELETE 或 SELECT FOR UPDATE 的操作将被阻塞,直到当前事务结束。此外,如果来自另一个事务的 UPDATE、DELETE 或 SELECT FOR UPDATE 已经锁定了某个被选中的行,SELECT FOR UPDATE 将等待该其他事务完成,然后锁定并返回更新后的行(如果该行已被删除,则不返回任何行)。而在 REPEATABLE READ 或 SERIALIZABLE 事务中,如果要锁定的行自事务开始以来已被更改,则会抛出一个错误。更多讨论见第 13 章。
FOR SHARE 的行为类似,但它在每个被检索的行上获取共享锁而非排他锁。共享锁阻塞其他事务对这些行执行 UPDATE、DELETE 或 SELECT FOR UPDATE,但不会阻止它们执行 SELECT FOR SHARE。
为了防止该操作等待其他事务提交,可使用NOWAIT 选项。使用NOWAIT时,如果选中的行不能被立即锁定,该语句会直接报错而不是等待。注意,NOWAIT只适用于行级锁;所需的ROW SHARE表级锁仍会按常规方式取得(见第 13 章)。如果想要不等待的表级锁,你可以先使用带NOWAIT的LOCK。
如果在 FOR UPDATE 或 FOR SHARE 中提到了特定的表,则只有来自那些表的行会被锁定;SELECT 中用到的任何其他表还是被简单地照常读取。一个没有表列表的 FOR UPDATE 或 FOR SHARE 子句会影响该语句中用到的所有表。如果把 FOR UPDATE 或 FOR SHARE 应用到一个视图或子查询,它会影响该视图或子查询中用到的所有表。However, FOR UPDATE/FOR SHARE do not apply to WITH queries referenced by the primary query. If you want row locking to occur within a WITH query, specify FOR UPDATE or FOR SHARE within the WITH query.
如果有必要对不同的表指定不同的锁定行为,可以写多个 FOR UPDATE 和 FOR SHARE 子句。如果同一个表同时被 FOR UPDATE 和 FOR SHARE 子句提到(或被隐式地影响到),那么它会按 FOR UPDATE 处理。类似地,如果在任何影响一个表的子句中指定了 NOWAIT,就会按此行为处理该表。
在返回的行无法清楚地与各个表行对应起来的上下文中,不能使用 FOR UPDATE 和 FOR SHARE;例如它们不能与聚合一起使用。
当 FOR UPDATE 或 FOR SHARE 出现在一个 SELECT 查询的顶层时,被锁定的行正好就是该查询返回的行;在连接查询的情况下,被锁定的行是那些对返回的连接行有贡献的行。此外,自该查询的快照起满足查询条件的行将被锁定,即使它们在该快照之后被更新并因此不再满足查询条件,也不会被返回。如果使用了 LIMIT,一旦返回的行数已满足限制,锁定就会停止(但注意被 OFFSET 跳过的行也会被锁定)。类似地,如果在游标的查询中使用 FOR UPDATE 或 FOR SHARE,只有游标实际取出或越过的行才会被锁定。
当 FOR UPDATE 或 FOR SHARE 出现在一个子 SELECT 中时,被锁定的行是子查询返回给外层查询的行。这可能比只检查子查询本身所显示的要少,因为来自外层查询的条件可能被用来优化子查询的执行。例如,
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;
将只锁定 col1 = 5 的行,即使该条件在文本上并不在子查询之内。
避免锁定一行然后又在后面的保存点或 PL/pgSQL 异常块中修改它。后续的回滚会导致锁丢失。例如:
BEGIN; SELECT * FROM mytable WHERE key = 1 FOR UPDATE; SAVEPOINT s; UPDATE mytable SET ... WHERE key = 1; ROLLBACK TO s;
在执行 ROLLBACK 之后,该行实际上已被解锁,而不是回到保存点之前已锁定但未修改的状态。如果当前事务中锁定的行被更新或删除,或者共享锁被升级为排他锁,就会出现这种风险:在所有这些情况下,之前的锁状态都会被遗忘。如果事务随后回滚到原始锁定命令和后续更改之间的某个状态,该行将看起来完全没有被锁定。这是一个实现上的缺陷,将在 PostgreSQL 的未来版本中解决。
一个运行在 READ COMMITTED 事务隔离级别并且使用 ORDER BY 和 FOR UPDATE/SHARE 的 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
它可以作为顶层命令使用,也可以作为复杂查询某些部分中一种节省空间的语法变体。
连接表 films 和表 distributors:
SELECT f.title, f.did, d.name, f.date_prod, f.kind
FROM distributors d, films f
WHERE f.did = d.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
这个例子展示了如何使用一个简单的 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 节)。
当然,SELECT语句与 SQL 标准兼容。但它也有一些扩展和缺失的特性。
FROM子句PostgreSQL 允许省略 FROM 子句。它可以直接用于计算简单表达式的结果:
SELECT 2+2;
?column?
----------
4
其他一些 SQL 数据库必须引入一个只有一行的虚拟表,才能从该表执行 SELECT。
请注意,如果未指定 FROM 子句,查询就不能引用任何数据库表。例如,以下查询是无效的:
SELECT distributors.* WHERE distributors.name = 'Westward';
PostgreSQL 在 8.1 之前的版本会接受这种形式的查询,并为查询引用的每个表在该查询的 FROM 子句中添加一个隐式项。现在不再允许这种做法。
AS关键词在 SQL 标准中,只要新列名是一个合法的列名(就是说与任何保留关键词不同),就可以省略输出列名之前的可选关键词AS。PostgreSQL要稍微严格些:只要新列名匹配任何关键词(保留或者非保留)就需要AS。推荐的习惯是使用 AS或者带双引号的输出列名来防止与未来增加的关键词可能的冲突。
在FROM项中,标准和 PostgreSQL都允许在非保留关键字别名前省略AS。但是由于语法歧义,这种写法无法用于输出列名。
ONLY与继承SQL 标准要求在使用ONLY时用圆括号括起表名,例如SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...。PostgreSQL认为这些圆括号是可选的。
PostgreSQL允许在末尾写上*,显式指定包含子表的非ONLY行为。标准不允许这种写法。
(这些要点同样适用于所有支持ONLY选项的 SQL 命令。)
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 标准指定了应该要识别的额外情况。
WINDOW子句的限制SQL 标准为窗口frame_clause提供了更多选项。PostgreSQL目前仅支持上面列出的选项。
LIMIT和OFFSETLIMIT和OFFSET子句是PostgreSQL特有的语法,MySQL也使用这种语法。SQL:2008 标准引入了OFFSET ... FETCH {FIRST|NEXT} ...子句来实现相同功能,如上面的LIMIT Clause所示。IBM DB2也使用这种语法。(为Oracle编写的应用经常采用一种变通办法,通过自动生成的rownum列实现这些子句的效果,而 PostgreSQL 中没有这一列。)
FOR UPDATE and FOR SHARE尽管 FOR UPDATE 出现在 SQL 标准中,但标准只允许它作为 DECLARE CURSOR 的一个选项。PostgreSQL 允许它出现在任何 SELECT 查询以及子 SELECT 中,但这是一个扩展。FOR SHARE 变体以及 NOWAIT 选项未出现在标准中。
WITH中的数据修改语句PostgreSQL允许将INSERT、UPDATE和DELETE用作WITH查询。SQL 标准中没有这种用法。
DISTINCT ON子句未在 SQL 标准中定义。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。