SELECT
SELECT, TABLE, WITH — 从一个表或视图检索行
大纲:
[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
[ * | expression [ [ AS ] output_name ] [, ...] ]
[ FROM from_item [, ...] ]
[ WHERE condition ]
[ GROUP BY 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 ] [...] ]
其中 from_item 可以是以下之一:
[ 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 [ NATURAL ] join_type from_item [ ON join_condition | USING
( join_column [, ...] ) ]
并且 grouping_element 可以是以下之一:
( )
expression
( expression [, ...] )
ROLLUP ( { expression | ( expression [, ...] ) } [, ...] )
CUBE ( { expression | ( expression [, ...] ) } [, ...] )
GROUPING SETS ( grouping_element [, ...] )
并且 with_query 是:
with_query_name [ ( column_name [, ...] ) ] AS [ [ NOT ] MATERIALIZED ]
( select | values | insert | update | delete )
TABLE [ ONLY ] table_name [ * ]
描述:
SELECT从零或更多表中检索行。SELECT的通常处理如下:
-
WITH列表中的所有查询都会被计算。这些查询实际充当了在FROM列表中可以引用的临时表。在FROM中被引用多次的WITH查询只会被计算一次,除非另有说明,否则NOT MATERIALIZED。 (见下文的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来显式地消除重复行。注意虽然 ALL是SELECT自身的默认行为,但这里DISTINCT是默认行为(见下文的 UNION Clause、INTERSECT Clause以及 EXCEPT Clause)。
-
如果指定了ORDER BY子句,被返回的行会以指定的顺序排序。如果没有给定ORDER BY,系统会以能最快产生行的顺序返回它们(见下文的 ORDER BY Clause)。
-
如果指定了LIMIT(或FETCH FIRST) 或者OFFSET子句,SELECT 语句只返回结果行的一个子集(见下文的LIMIT Clause)。
-
如果指定了FOR UPDATE、 FOR NO KEY UPDATE、 FOR SHARE 或者FOR KEY SHARE, SELECT语句会把被选中的行锁定而不让并发更新访问它们(见下文的The Locking Clause)。
你必须拥有在一个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查询的结果。
RECURSIVE的另一个效果是 WITH查询不需要被排序:一个查询可以引用另一个在列表中比它靠后的查询(不过,循环引用或者互递归没有实现)。 如果没有RECURSIVE,WITH 查询只能引用在WITH列表中位置更前面的兄弟 WITH查询。
当WITH子句中有多个查询时,RECURSIVE应只编写一次,紧跟在WITH之后。它适用于WITH子句中的所有查询,尽管它对不使用递归或前向引用的查询没有影响。
主查询以及WITH查询全部(理论上)在同一时间被执行。这意味着从该查询的任何部分都无法看到 WITH中的一个数据修改语句的效果,不过可以读 取其RETURNING输出。如果两个这样的数据修改语句尝试修改相同的行,结果将无法确定。
WITH查询的一个关键属性是,即使主查询多次引用它们,它们通常每次执行主查询只计算一次。特别是,数据修改语句确保执行一次而且只执行一次,而与主查询是否读取它们的全部或任何输出无关。
但是,WITH查询可以标记为NOT MATERIALIZED以移除此保证。在这种情况下,WITH查询可以折叠到主查询中,就好像它是主查询的FROM子句中的简单的sub-SELECT。如果主查询多次引用WITH查询,则会导致重复计算;但是,如果每次此类使用只需要WITH查询的总输出中的几行,NOT MATERIALIZED可以通过允许查询联合优化来节省开销。NOT MATERIALIZED被忽略,如果它被附加到一个递归的WITH查询,或者不是边际效应无关的(也就是说,不是包含非易失性函数的普通的SELECT)。
FROM 子句
FROM子句为SELECT 指定一个或者更多源表。如果指定了多个源表,结果将是所有源表的笛卡尔积(交叉连接)。但是通常会增加限定条件(通过 WHERE)来把返回的行限制为该笛卡尔积的一个小子集。
FROM子句可以包含下列元素:
table_name
一个现有表或视图的名称(可以是模式限定的)。如果在表名前指定了 ONLY,则只会扫描该表。如果没有指定 ONLY,该表及其所有后代表(如果有)都会被扫描。可选地,可以在表名后指定*来显式地指示包括后代表。
alias
一个包含别名的FROM项的替代名称。别名被用于让书写简洁或者消除自连接中的混淆(其中同一个表会被扫描多次)。当提供一个别名时,表或者函数的实际名称会被隐藏。例如,给定FROM foo AS f,SELECT的剩余部分就必须以 f而不是foo来引用这个FROM项。如果写了一个别名,还可以写一个列别 名列表来为该表的一个或者多个列提供替代名称。
TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] table_name之后的 TABLESAMPLE子句表示应该用指定的 sampling_method 来检索表中行的子集。这种采样优先于任何其他过滤器(例如 WHERE子句)。Halo包括两种采样方法:BERNOULLI和SYSTEM,其他采样方法可以通过扩展安装在数据库中。
BERNOULLI以及SYSTEM采样方法都接受 一个参数,它表示要采样的表 的分数,表示为一个 0 到 100 之间的百分数。这个参数可以是任意的实数值表达式(其他的采样方法可能接受更多或者不同的参数)。这两种方法都返回一个随机选取的该表采样,其中包含了指定百分数的表行。BERNOULLI方法扫描整个表并且 用指定的几率选择或者忽略行。SYSTEM方法会做块层的采样,每个块都有指定的机会能被选中,被选中块中的所有行都会被返回。在指定较小的采样百分数时,SYSTEM 方法要比BERNOULLI方法快很多,但是前者可能由于聚簇效应返回随机性较差的表采样。
可选的REPEATABLE子句指定一个用于产生采样方法中随机数的种子数或表达式。种子值可以是任何非空浮点值。如果查询时表没有被更改,指定相同种子和argument值的两个查询将会选择该表相同的采样。但是不同的种子值通常将会产生不同的采样。如果没有给出REPEATABLE,则会基于一个系统产生的种子为每一个查询选择一个新的随机采样。
注意有些扩展采样方法不接受REPEATABLE,并且将总是为每一次使用产生新的采样。
select
一个子-SELECT可以出现在 FROM子句中。这就好像把它的输出创建为一个存在于该SELECT命令期间的临时表。注意子-SELECT必须用圆括号包围,并且必须为它提供一个别名。也可以在这里使用一个VALUES命令。
with_query_name
可以通过写一个WITH查询的名称来引用它,就好像该查询的名称是一个表名(实际上,该WITH查询会为主查询隐藏任何具有相同名称的真实表。如果必要,你可以使用带模式限定的方式以相同的名称来引用真实表)。可以像表一样,以同样的方式提供一个别名。
function_name
函数调用可以出现在FROM子句中(对于返回结果集合的函数特别有用,但是可以使用任何函数)。这就好像把该函数的输出创建为一个存在于该SELECT命令期间的临时表。
当为该函数调用增加可选的 WITH ORDINALITY子句时,会在该函数的输出列之后追加一个新的列来为每一行编号。
可以用和表一样的方式提供一个别名。如果写了一个别名,还可以写一个 列别名列表来为该函数的组合返回类型的一个或者多个属性提供替代名称, 包括由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
One of
• [ INNER ] JOIN
• LEFT [ OUTER ] JOIN
• RIGHT [ OUTER ] JOIN
• FULL [ OUTER ] JOIN
• CROSS JOIN
对于INNER和OUTER连接类型,必须指定 一个连接条件,即NATURAL、ON join_condition或者 USING (join_column [, ...]) 之一(只能有一种)。其含义见下文。对于 CROSS JOIN,上述子句不能出现。
一个JOIN子句联合两个FROM项(为了方便我们称之为“表”,尽管实际上它们可以是任何类型的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_condition
join_condition 是一个会得到boolean类型值的表达式(类似于一个 WHERE子句),它说明一次连接中哪些行被认为相匹配。
USING ( join_column [, ...] )
形式USING ( a, b, ... )的子句是 ON left_table.a = right_table.a AND left_table.b = right_table.b ...的简写。还有,USING表示每一对相等列中只有一个会被包括在连接输出中。
NATURAL
NATURAL是一个USING列表的速记,该列表中提到两个表中具有匹配名称的所有的列。如果没有公共列名,则NATURAL等效于ON TRUE。
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 LATERAL Y这样的结构在语法上是合法的,但实际上不允许用于在Y中引用 X。
WHERE 子句
可选的WHERE子句的形式
WHERE condition
其中condition 是任一计算得到布尔类型结果的表达式。任何不满足这个条件的行都会从输出中被消除。如果用一行的实际值替换其中的变量引用后,该表达式返回真,则该行符合条件。
GROUP BY 子句
可选的GROUP BY子句的形式
GROUP BY grouping_element [, ...]
GROUP BY将会把所有被选择的行中共享相同分组表达式值的那些行压缩成一个行。一个被用在 grouping_element中的 expression可以是输入列名、输出列 (SELECT列表项)的名称或序号或者由输入列值构成的任意表达式。在出现歧义时,GROUP BY名称 将被解释为输入列名而不是输出列名。
如果任何GROUPING SETS、ROLLUP或者 CUBE作为分组元素存在,则GROUP BY子句整体上定义了数个独立的分组集。其效果等效于在子查询间构建一个UNION ALL,子查询带有分组集作为它们的GROUP BY子句。
聚集函数(如果使用)会在组成每一个分组的所有行上进行计算,从而为每 一个分组产生一个单独的值(如果有聚集函数但是没有 GROUP BY子句,则查询会被当成是由所有选中行构成 的一个单一分组)。传递给每一个聚集函数的行集合可以通过在聚集函数调用附加一个FILTER子句来进一步过滤。当存在一个 FILTER子句时,只有那些匹配它的行才会被包括在该聚集函数的输入中。
当存在GROUP BY子句或者任何聚集函数时,SELECT列表表达式不能引用非分组列(除非它出现在聚集函数中或者它函数依赖于分组列),因为这样做会导致返回非分组列的值时会有多种可能的值。如果分组列是包含非分组列的表的主键(或者主键的子集),则存在函数依赖。
记住所有的聚集函数都是在HAVING子句或者 SELECT列表中的任何“标量”表达式之前被计算。这意味着一个CASE表达式不能被用来跳过一个聚集表达式的计算。
当前,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的情况下指定该子句。新窗口总是使用它自己的帧子句,被复制的窗口不必指定一个帧子句。
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的选择在上面of 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列。那么该帧包含的行的排序列值不超过offset且小于(对于PRECEDING)或者大于(对于FOLLOWING)当前行的排序列值。在这些
情况中,offset表达式的数据类型取决于排序列的数据类型。对于数字排序列,它通常与排序列是相同类型,但对于datetime类型的排序列它是interval。在所有这些情况中,offset的值必须是非空和非负。此外,虽然offset并非必须是简单常量,但它不能包含变量、聚集函数或者窗口函数。
frame_exclusion选项允许从帧中排除当前行周围的行,即便根据帧的起始选项来说它们应该被包含在帧中。EXCLUDE CURRENT ROW把当前行从帧中排除。EXCLUDE GROUP把当前行和它在排序上的平级行从帧中排除。EXCLUDE TIES从帧中排除当前行的任何平级行,但是不排除当前行本身。EXCLUDE NO OTHERS只是明确地指定不排除当前行或其平级行的默认行为。
注意,如果ORDER BY排序无法把行唯一地排序,则ROWS模式可能产生不可预测的结果。RANGE以及GROUPS模式的目的是确保在ORDER BY顺序中平等的行被同样对待:一个给定平级组中的所有行将在一个帧中或者被从帧中排除。
WINDOW子句的目的是指定出现在查询的 SELECT list或 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( 当期望的输出名称不和Halo任何关键词冲突的情况下,可以省略AS。为了避免和未来增加的关键词冲突,推荐总是写上AS或者用双引号引用输出名称)。如果你不指定列名,Halo会自动选择一个名称。如果列的表达式是一个简单的列引用,那么被选择的名称就和该列的名称相同。在使用函数或者类型名称的更复杂的情况中,系统可能会生成诸如 ?column? 之类的名称。
一个输出列的名称可以被用来在ORDER BY以及 GROUP BY子句中引用该列的值,但是不能用于WHERE和HAVING子句(在其中必须写出表达式)。
可以在输出列表中写来取代表达式,它是被选中行的所有列的一种简写方式。还可以写table_name.,它是只来自那个表的所有列的简写形式。在这些情况中无法用 AS指定新的名称,输出行的名称将和表列的名称相同。
根据 SQL 标准,输出列表中的表达式应该在应用DISTINCT、ORDER BY或者LIMIT之前计算。
在使用DISTINCT时显然必须这样做,否则就无法搞清到底在区分什么值。不过,在很多情况下如果先计算ORDER BY和LIMIT再计算输出表达式会很方便,特别是如果输出列表中包含任何 volatile 函数或者代价昂贵的函数时尤其如此。通过这种行为,函数计算的顺序更加直观并且对于从未出现在输出中的行将不会进行计算。只要输出表达式没有被DISTINCT、ORDER BY或者GROUP BY引用,Halo实际将在排序和限制行数之后计算输出表达式(作为一个反例,SELECT f(x) FROM tab ORDER
BY 1显然必须在排序之前计算f(x))。包含有集合返回函数的输出表达式实际是在排序之后和限制行数之前被计算,这样LIMIT才能切断来自集合返回函数的输出。
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可以写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语句的结果中但不在右 SELECT语句结果中的行集合。
EXCEPT的结果不会包含重复行,除非指定了 ALL选项。如果有ALL,一个在左表中有 m次重复并且在右表中有 n次重复的行将会在结果集中出现 max(m-n,0) 次。DISTINCT可以写DISTINCT来显式地指定消除重复行的行为。
除非用圆括号指定计算顺序,同一个SELECT语句中的多个 EXCEPT操作符会从左至右计算。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子句导致结果行被按照指定的表达式排序。如果两行按照最左边的表达式是相等的,则会根据下一个表达式比较它们,依次类推。如果按照所有指定的表达式它们都是相等的,则它们被返回的顺序取决于实现。
每一个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"。
LIMIT 子句
LIMIT子句由两个独立的子句构成:
LIMIT { count | ALL }
OFFSET start
参数count指定要返回 的最大行数,而start 指定在返回行之前要跳过的行数。在两者都被指定时,在开始计算要返回的 count行之前会跳过 start行。
如果count表达式计算 为 NULL,它会被当成LIMIT ALL,即没有限制。如果 start计算为NULL,它会被当作OFFSET 0。
SQL:2008 引入了一种不同的语法来达到相同的结果,Halo也支持它:
OFFSET start { ROW | ROWS }
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }
在这种语法中,标准要求start或count是一个文本常量、一个参数或者一个变量名。而作为Halo的一种的扩展,还允许其他的表达式,但通常需要被封闭在圆括号中以避免歧义。如果在一个 FETCH子句中省略 count,它的默认值为 1。 WITH TIES选项用于根据ORDER BY子句返回与结果集中最后一个位置相关的任何附加行; ORDER BY在这种情况下是强制性的。 ROW和ROWS以及 FIRST和NEXT是噪声,它们不影响这些子句的效果。根据标准,如果都存在,OFFSET子句 必须出现在FETCH子句之前。但是Halo更宽松,它允许两种顺序。在使用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 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
为了防止该操作等待其他事务提交,可使用NOWAIT或者 SKIP LOCKED选项。使用NOWAIT时,如果选中的行不能被立即锁定,该语句会报告错误而不是等待。使用 SKIP LOCKED时,无法被立即锁定的任何选中行都 会被跳过。跳过已锁定行会提供数据的一个不一致的视图,因此这不适合于一般目的的工作,但是可以被用来避免多个用户访问一个类似队列的表时出现锁竞争。注意NOWAIT和 SKIP LOCKED只适合行级锁 — 所要求的
ROW SHARE表级锁
仍然会以常规的方式取得。如果想要不等待的表级锁,你可以先使用带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, 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
这里是带有增加的序数列的函数的例子:
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,然后是查询的递归部分。
要确保查询的递归部分最终将不返回任何行,否则该查询将无限循环这个例子使用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;