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_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 | WITH TIES } ] [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OFfrom_reference[, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ] wherefrom_itemcan 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_itemjoin_typefrom_item{ ONjoin_condition| USING (join_column[, ...] ) [ ASjoin_using_alias] }from_itemNATURALjoin_typefrom_itemfrom_itemCROSS JOINfrom_itemandgrouping_elementcan be one of: ( )expression(expression[, ...] ) ROLLUP ( {expression| (expression[, ...] ) } [, ...] ) CUBE ( {expression| (expression[, ...] ) } [, ...] ) GROUPING SETS (grouping_element[, ...] ) andwith_queryis: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_valueDEFAULTcycle_mark_default] USINGcycle_path_col_name] 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。)尽管查询输出列名义上是在下一步中计算的,但它们也可以在 GROUP BY 子句中被引用(通过名称或序号)。
实际的输出行是使用 SELECT 输出表达式针对每个被选中的行或行组计算出来的。(参见下文的 SELECT List。)
SELECT DISTINCT 会从结果中去除重复的行。SELECT DISTINCT ON 会去除在所有指定表达式上都匹配的行。SELECT ALL(默认)将返回所有候选行,包括重复的行。(参见下文的 DISTINCT Clause。)
使用操作符 UNION、INTERSECT 和 EXCEPT,可以把多个 SELECT 语句的输出组合起来形成一个单一的结果集。UNION 操作符返回出现在其中一个或两个结果集中的所有行。INTERSECT 操作符返回严格同时存在于两个结果集中的所有行。EXCEPT 操作符返回位于第一个结果集中但不在第二个结果集中的行。在这三种情况下,除非指定了 ALL,否则都会去除重复的行。可以加上噪声词 DISTINCT 来显式指定去除重复的行。注意,尽管在这里 DISTINCT 是默认行为,而 ALL 是 SELECT 本身的默认值。(参见下文的 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 或 MERGE 语句。当编写数据修改语句(INSERT、UPDATE、DELETE 或 MERGE)并置于 WITH 中时,通常要包含一个 RETURNING 子句。构成由主查询读取的临时表的,是 RETURNING 的输出,而非该语句所修改的底层表。如果省略了 RETURNING,该语句仍会被执行,但它不产生输出,因此无法被主查询当作表来引用。
必须为每个 WITH 查询指定一个名称(不带模式限定)。还可以选择指定一个列名列表;如果省略,列名将从子查询中推断出来。
如果指定了 RECURSIVE,它允许一个 SELECT 子查询按名称引用自身。这样的子查询必须具有如下形式
non_recursive_termUNION [ ALL | DISTINCT ]recursive_term
其中递归自引用必须出现在 UNION 的右侧。每个查询只允许一个递归自引用。不支持递归的数据修改语句,但你可以在数据修改语句中使用递归 SELECT 查询的结果。示例请参见 Section 7.8。
RECURSIVE 的另一个效果是 WITH 查询不必有序:一个查询可以引用列表中位于其后的另一个查询。(不过,循环引用或相互递归并没有实现。)如果没有 RECURSIVE,WITH 查询只能引用 WITH 列表中位于其之前的同级 WITH 查询。
当 WITH 子句中有多个查询时,RECURSIVE 应该只写一次,紧跟在 WITH 之后。它适用于 WITH 子句中的所有查询,但对不使用递归或前向引用的查询没有影响。
可选的 SEARCH 子句会计算一个搜索序列列,该列可用于按广度优先或深度优先的顺序排列递归查询的结果。所提供的列名列表指定了用于跟踪已访问行的行键。一个名为 search_seq_col_name 的列将被添加到 WITH 查询的结果列列表中。在外层查询中可以按该列排序来实现相应的排序。示例请参见 Section 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 查询的结果列列表中。该列在内部用于跟踪已访问的行。示例请参见 Section 7.8.2.2。
SEARCH 子句和 CYCLE 子句都只对递归的 WITH 查询有效。with_query 必须是一个 UNION(或 UNION ALL),其操作数为两个 SELECT(或等效)命令(不能有嵌套的 UNION)。如果同时使用了这两个子句,SEARCH 子句添加的列会出现在 CYCLE 子句添加的列之前。
主查询和 WITH 查询都(在概念上)同时执行。这意味着,WITH 中数据修改语句的效果无法从查询的其他部分看到,除非通过读取它的 RETURNING 输出。如果两个这样的数据修改语句试图修改同一行,其结果是未定义的。
WITH 查询的一个关键特性是,它们通常在主查询的每次执行中只被计算一次,即使主查询不止一次地引用它们。特别地,数据修改语句保证会被执行且仅执行一次,而不管主查询是否读取了它们的全部或任何输出。
不过,可以将 WITH 查询标记为 NOT MATERIALIZED 来去掉这一保证。在这种情况下,该 WITH 查询可以被折叠进主查询,非常类似于一个简单的子 SELECT 位于主查询的 FROM 子句中一样。如果主查询不止一次地引用该 WITH 查询,这会导致重复计算;但如果每一次这样的使用只需要该 WITH 查询总输出中的少量行,NOT MATERIALIZED 可以通过允许对查询进行联合优化而带来净收益。如果 NOT MATERIALIZED 被附加到一个递归的或者并非无副作用的 WITH 查询上(即该查询不是一个不含易失函数的普通 SELECT),它将被忽略。
默认情况下,如果一个无副作用的 WITH 查询在主查询的 FROM 子句中恰好被使用了一次,它就会被折叠进主查询。在这种做法在语义上应当不可见的情形下,这允许对两个查询层级进行联合优化。不过,可以通过把 WITH 查询标记为 MATERIALIZED 来阻止这种折叠。例如,如果该 WITH 查询被用作优化屏障来防止规划器选择糟糕的计划,这样做可能有用。v12 之前的 PostgreSQL 版本从不进行这样的折叠,因此为旧版本编写的查询可能依赖 WITH 充当优化屏障。
更多信息请参见 Section 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 ) ]TABLESAMPLE 子句跟在一个 table_name 之后,表示应当使用指定的 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。
可以像对表那样提供别名。如果写了别名,还可以写一个列别名列表,为函数复合返回类型的一个或多个属性提供替代名称,包括序数列(如果存在的话)。
多个函数调用可以被组合成单个 FROM 子句项,方法是用 ROWS FROM( ... ) 把它们包围起来。这样一个项的输出是每个函数的第一行、然后每个函数的第二行等等的串联。如果某些函数产生的行比其他函数少,则用空值替代缺失的数据,这样返回的总行数总是与产生最多行的函数相同。
如果函数被定义为返回 record 数据类型,则必须存在一个别名或关键字 AS,其后跟一个形如 ( 的列定义列表。列定义列表必须与函数实际返回的列的数量和类型相匹配。column_name data_type [, ... ])
在使用 ROWS FROM( ... ) 语法时,如果其中一个函数需要列定义列表,首选的做法是把列定义列表放在 ROWS FROM( ... ) 内部、紧跟在函数调用之后。只有当 ROWS FROM( ... ) 中仅有一个函数且没有 WITH ORDINALITY 子句时,列定义列表才可以被放在这个结构之后。
要把 ORDINALITY 与列定义列表一起使用,必须使用 ROWS FROM( ... ) 语法,并把列定义列表放在 ROWS FROM( ... ) 内部。
join_type以下之一:
[ INNER ] JOIN
LEFT [ OUTER ] JOIN
RIGHT [ OUTER ] JOIN
FULL [ OUTER ] JOIN
For the INNER and OUTER join types, a
join condition must be specified, namely exactly one of
ON ,
join_conditionUSING (,
or join_column [, ...])NATURAL. See below for the meaning.
JOIN 子句把两个 FROM 项组合起来,为方便起见,我们把这两者称为“表”,尽管实际上它们可以是任何类型的 FROM 项。如有必要,请使用圆括号来确定嵌套的顺序。在没有圆括号的情况下,JOIN 从左到右嵌套。无论如何,JOIN 的结合优先级都高于用于分隔 FROM 列表项的逗号。所有 JOIN 选项都只是记号上的便利,因为它们并没有提供任何用普通 FROM 和 WHERE 无法实现的功能。
LEFT OUTER JOIN 返回通过了限定条件的笛卡尔积中的所有行(即所有通过其连接条件的组合行),再加上左表中没有通过该连接条件的右侧行与之配对的每一行的一份拷贝。这个左侧行通过为右侧的列插入空值,被扩展到被连接表的完整宽度。注意,在决定哪些行有匹配时,只考虑 JOIN 子句自身的条件。外层条件会在此之后被应用。
相反,RIGHT OUTER JOIN 返回所有被连接的行,再加上每个未匹配的右侧行的一行(在左侧用空值扩展)。这只是一种记号上的便利,因为可以通过交换左右表把它转换成 LEFT OUTER JOIN。
FULL OUTER JOIN 返回所有被连接的行,再加上每个未匹配的左侧行的一行(在右侧用空值扩展),再加上每个未匹配的右侧行的一行(在左侧用空值扩展)。
ON join_conditionjoin_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 列表中的名称相同。
NATURALNATURAL 是一种简写,它相当于一个列出了两个表中所有名称相匹配的列的 USING 列表。如果没有共同的列名,NATURAL 就等价于 ON TRUE。
CROSS JOINCROSS JOIN 等价于 INNER JOIN ON (TRUE),即没有任何行会被限定条件去除。它们产生一个简单的笛卡尔积,与在 FROM 顶层列出这两个表所得到的结果相同,但会受连接条件(如果有)的限制。
LATERALLATERAL 关键字可以放在一个子 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 YY 引用 X。
WHERE 子句可选的 WHERE 子句具有如下一般形式
WHERE condition
其中 condition 是任何能计算出 boolean 类型结果的表达式。任何不满足该条件的行都会从输出中被去除。如果用实际的行值替换任何变量引用后它返回真,则该行满足该条件。
GROUP BY 子句可选的 GROUP BY 子句具有如下一般形式
GROUP BY [ ALL | DISTINCT ] grouping_element [, ...]
GROUP BY 会把在分组表达式上具有相同值的全部被选中行压缩成单一的一行。expression 若用在 grouping_element 内部,可以是一个输入列的名称,或者一个输出列(SELECT 列表项)的名称或序号,或者由输入列的值构成的任意表达式。在存在歧义的情况下,GROUP BY 中的名称将被解释为输入列名,而不是输出列名。
如果 GROUPING SETS、ROLLUP 或 CUBE 中有任何一个作为分组元素出现,那么 GROUP BY 子句整体上就定义了若干个独立的 grouping sets。其效果等同于在各个子查询之间构造一个 UNION ALL,每个子查询以各自的分组集作为它的 GROUP BY 子句。可选的 DISTINCT 子句会在处理之前去除重复的集合;它 不会 把 UNION ALL 转变成 UNION DISTINCT。关于分组集处理的更多细节请参见 Section 7.2.4。
如果使用了聚合函数,它们会对组成每个组的所有行进行计算,并为每个组产生一个单独的值。(如果有聚合函数但没有 GROUP BY 子句,则该查询会被视为具有一个由全部被选中行组成的单一组。)通过在聚合函数调用上附加一个 FILTER 子句,可以进一步过滤输入给每个聚合函数的行集合;更多信息请参见 Section 4.2.7。当存在 FILTER 子句时,只有与它匹配的那些行才会被包括在该聚合函数的输入中。
当存在 GROUP BY 或任何聚合函数时,SELECT 列表表达式引用未分组的列就是无效的,除非是在聚合函数内部,或者该未分组列在函数上依赖于分组列,因为否则对于一个未分组列将会有多于一个可能的返回值。如果分组列(或其一个子集)是包含该未分组列的表的主键,则存在函数依赖。
请记住,所有的聚合函数都会在计算任何“标量”表达式之前被计算,这些表达式位于 HAVING 子句或 SELECT 列表中。这意味着,例如,不能用一个 CASE 表达式来跳过聚合函数的计算;参见 Section 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 中引用的每一列都必须无歧义地引用一个分组列,除非该引用出现在聚合函数内部,或者该未分组列在函数上依赖于分组列。
HAVING 的存在会把一个查询变成分组查询,即使没有 GROUP BY 子句。这与查询包含聚合函数但没有 GROUP BY 子句时发生的情况相同。所有被选中的行都被视为构成一个单一组,并且 SELECT 列表和 HAVING 子句只能在聚合函数内部引用表列。这样的查询将在 HAVING 条件为真时发出一行,在条件不为真时发出零行。
目前,FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 和 FOR KEY SHARE 不能与 HAVING 一起指定。
WINDOW 子句可选的 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 子句中的元素大体相同,只是它们总是简单的表达式,而绝不会是输出列的名称或编号。另一个区别是,这些表达式可以包含聚合函数调用,而这在常规的 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 PRECEDINGoffsetPRECEDING CURRENT ROWoffsetFOLLOWING 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 列。此时,帧包含的是那些排序列值与当前行的排序列值相差不超过 offset 的行(小于是对 PRECEDING 而言,大于是对 FOLLOWING 而言)。在这些情况下,offset 表达式的数据类型取决于排序列的数据类型。对于数值排序列,它通常与排序列的类型相同;但对于日期时间排序列,它是一个 interval 类型。在所有这些情况下,offset 的值必须是非空且非负的。此外,虽然 offset 不必是一个简单的常量,但它不能包含变量、聚合函数或窗口函数。
frame_exclusion 选项允许把当前行周围的行从帧中排除,即使根据帧开始和帧结束选项它们本应被包括在内。EXCLUDE CURRENT ROW 把当前行从帧中排除。EXCLUDE GROUP 把当前行及其排序同位行从帧中排除。EXCLUDE TIES 把当前行的任何同位行从帧中排除,但不排除当前行本身。EXCLUDE NO OTHERS 只是显式地指定默认行为,即不排除当前行或其同位行。
注意,ROWS 模式在 ORDER BY 排序不能唯一地排列各行时可能产生不可预测的结果。RANGE 和 GROUPS 模式的设计目的是确保在 ORDER BY 排序中互为同位行的行被同等对待:一个给定同位行组的所有行要么都在帧中,要么都被排除在帧之外。
WINDOW 子句的目的是指定窗口函数的行为,这些窗口函数出现在查询的 SELECT list或 ORDER BY 子句中。这些 WINDOW 子句条目可以由这些函数在它们的 OVER 子句中按名称引用。不过,WINDOW 子句条目并不一定要在任何地方被引用;如果它在查询中没有被使用,它就只是被简单地忽略。完全可以不使用任何 WINDOW 子句而使用窗口函数,因为窗口函数调用可以在它的 OVER 子句中直接指定它的窗口定义。不过,当不止一个窗口函数需要相同的窗口定义时,WINDOW 子句可以省去重复输入。
目前,FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 和 FOR KEY SHARE 不能与 WINDOW 一起指定。
窗口函数在 Section 3.5、Section 4.2.8 和 Section 7.2.5 中有详细描述。
SELECT 列表SELECT 列表(位于关键字 SELECT 和 FROM 之间)指定构成 SELECT 语句输出行的表达式。这些表达式可以(而且通常也确实会)引用在 FROM 子句中计算的列。
就像在表中一样,SELECT 的每一个输出列都有一个名称。在一个简单的 SELECT 中,这个名称只是用来为显示标注该列;但当这个 SELECT 是一个更大查询的子查询时,该名称会被更大的查询视为这个子查询所产生的虚拟表的列名。要为输出列指定名称,请在该列的表达式后面写 AS output_name。(你可以省略 AS,但仅当所需的输出名称不匹配任何 PostgreSQL 关键字时才行,参见 Appendix 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_statementUNION [ ALL | DISTINCT ]select_statement
select_statement 是任何 SELECT 语句,且不带 ORDER BY、LIMIT、FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 或 FOR KEY SHARE 子句。(ORDER BY 和 LIMIT 可以附加到一个子表达式上,前提是该子表达式被圆括号包围。如果没有圆括号,这些子句将被认为应用于 UNION 的结果,而不是其右侧的输入表达式。)
UNION 操作符计算所涉及的 SELECT 语句返回的行的集合并集。如果一个行出现在至少一个结果集中,它就位于这两个结果集的集合并集中。这两个 SELECT 语句作为 UNION 的直接操作数,必须产生相同数量的列,并且对应的列必须具有兼容的数据类型。
UNION 的结果不包含任何重复的行,除非指定了 ALL 选项。ALL 会阻止去除重复行。(因此,UNION ALL 通常比 UNION 快得多;在能用的时候请使用 ALL。)可以书写 DISTINCT 来显式指定去除重复行的默认行为。
多个 UNION 操作符出现在同一个 SELECT 语句中时,会从左到右进行计算,除非圆括号另作指示。
目前,FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 和 FOR KEY SHARE 既不能为 UNION 的结果指定,也不能为 UNION 的任何输入指定。
INTERSECT 子句INTERSECT 子句具有如下一般形式:
select_statementINTERSECT [ ALL | DISTINCT ]select_statement
select_statement 是任何 SELECT 语句,且不带 ORDER BY、LIMIT、FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 或 FOR KEY SHARE 子句。
INTERSECT 操作符计算所涉及的 SELECT 语句返回的行的集合交集。如果一个行同时出现在两个结果集中,它就位于这两个结果集的交集中。
INTERSECT 的结果不包含任何重复的行,除非指定了 ALL 选项。使用 ALL 时,在左表中有 m 个重复且在右表中有 n 个重复的行,将在结果集中出现 min(m,n) 次。可以书写 DISTINCT 来显式指定去除重复行的默认行为。
多个 INTERSECT 操作符出现在同一个 SELECT 语句中时,会从左到右进行计算,除非圆括号另有规定。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_statementEXCEPT [ ALL | DISTINCT ]select_statement
select_statement 是任何 SELECT 语句,且不带 ORDER BY、LIMIT、FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 或 FOR KEY SHARE 子句。
EXCEPT 操作符计算位于左侧 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 BYexpression[ ASC | DESC | USINGoperator] [ NULLS { FIRST | LAST } ] [, ...]
ORDER BY 子句使结果行按照指定的表达式进行排序。如果两行按照最左边的表达式相等,则按照下一个表达式比较它们,依此类推。如果按照所有指定的表达式它们都相等,那么它们将以取决于实现的方式排序返回。
每个 expression 可以是一个输出列(SELECT 列表项)的名称或序号,也可以是由输入列的值构成的任意表达式。
序号指的是输出列的(从左到右的)序数位置。这个特性使得可以基于一个没有唯一名称的列来定义排序。这从来都不是绝对必需的,因为总是可以使用 AS 子句为输出列分配一个名称。
在 ORDER BY 子句中也可以使用任意表达式,包括没有出现在 SELECT 输出列表中的列。因此下面的语句是有效的:
SELECT name FROM distributors ORDER BY code;
这个特性的一个限制是,ORDER BY 子句在应用于 UNION、INTERSECT 或 EXCEPT 子句的结果时,只能指定输出列的名称或序号,而不能指定表达式。
如果一个 ORDER BY 表达式是一个既匹配输出列名又匹配输入列名的简单名称,ORDER BY 会把它解释为输出列名。这与 GROUP BY 在相同情况下会做出的选择相反。造成这种不一致是为了与 SQL 标准保持兼容。
可以选择在任何表达式的后面加上关键字 ASC(升序)或 DESC(降序),该表达式位于 ORDER BY 子句中。如果没有指定,则默认假定为 ASC。或者,也可以在 USING 子句中指定一个特定的排序操作符名称。排序操作符必须是某个 B 树操作符族的小于或大于成员。ASC 通常等价于 USING <,而 DESC 通常等价于 USING >。(但用户定义数据类型的创建者可以确切地定义默认的排序顺序是什么,它可能对应于具有其他名称的操作符。)
如果指定了 NULLS LAST,空值将排在所有非空值之后;如果指定了 NULLS FIRST,空值将排在所有非空值之前。如果两者都没有指定,则默认行为是 NULLS LAST,前提是指定或隐含了 ASC;而 NULLS FIRST 适用于指定了 DESC 的情况(因此,默认行为就好像空值大于非空值一样)。当指定了 USING 时,默认的空值排序取决于该操作符是小于操作符还是大于操作符。
注意,排序选项只作用于它们所跟随的表达式;例如,ORDER BY x, y DESC 与 ORDER BY x DESC, y DESC 的含义并不相同。
字符串数据会根据应用于被排序列的排序规则进行排序。必要时可以通过一个 COLLATE 子句来覆盖它,该子句包含在 expression 中,例如 ORDER BY mycolumn COLLATE "en_US"。更多信息请参见 Section 4.2.10 和 Section 23.2。
LIMIT 子句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 | WITH TIES }
在这种语法中,标准要求 start 或 count 的值是一个字面常量、一个参数或一个变量名;作为 PostgreSQL 的扩展,也允许其他表达式,但通常需要用圆括号包围以避免歧义。如果 count 在一个 FETCH 子句中被省略,它默认为 1。WITH TIES 选项用于返回根据 ORDER BY 子句与结果集中最后一名并列的任何附加行;在这种情况下 ORDER BY 是强制性的,并且不允许 SKIP LOCKED。ROW 和 ROWS 以及 FIRST 和 NEXT 是不影响这些子句效果的噪声词。按照标准,如果两者同时存在,OFFSET 子句必须位于 FETCH 子句之前;但 PostgreSQL 更宽松,允许任意顺序。
在使用 LIMIT 时,最好使用一个能把结果行约束成唯一顺序的 ORDER BY 子句。否则你将得到查询各行的一个不可预测的子集 — 你可能想要第十到第二十行,但这是按什么顺序的第十到第二十行?除非你指定 ORDER BY,否则你并不知道是什么顺序。
查询规划器在生成查询计划时会考虑 LIMIT,因此根据你为 LIMIT 和 OFFSET 所使用的内容,你很可能得到不同的计划(产生不同的行顺序)。因此,使用不同的 LIMIT/OFFSET 值来选取查询结果的不同子集,将给出不一致的结果,除非你用 ORDER BY 强制一个可预期的结果顺序。这不是一个 bug;这是 SQL 不承诺以任何特定顺序交付查询结果这一事实的必然结果,除非使用 ORDER BY 来约束顺序。
同一个 LIMIT 查询在重复执行时甚至可能返回表的行的不同子集,前提是没有 ORDER BY 来强制选取一个确定性的子集。同样,这不是一个 bug;在这种情况下,根本不保证结果的确定性。
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 必须是一个表 alias,或者一个非隐藏的 table_name(它在 FROM 子句中被引用)。关于每种行级锁模式的更多信息,请参见 Section 13.3.2。
要防止该操作等待其他事务提交,可以使用 NOWAIT 或 SKIP LOCKED 选项。使用 NOWAIT 时,如果一个被选中的行不能立即被锁定,该语句将报告错误而不是等待。使用 SKIP LOCKED 时,任何不能立即被锁定的被选中行都会被跳过。跳过被锁定的行会提供数据的一种不一致视图,因此这不适合一般用途的工作,但可以用来避免多个消费者访问类似队列的表时的锁竞争。注意,NOWAIT 和 SKIP LOCKED 只适用于行级锁 — 所需的 ROW SHARE 表级锁仍会以普通方式获取(参见 Chapter 13)。如果你需要在不等待的情况下获取表级锁,可以先使用 LOCK 并带上 NOWAIT 选项。
如果在锁定子句中指定了特定的表,则只有来自那些表的行会被锁定;SELECT 中使用的任何其他表都只是像平常一样被读取。没有表列表的锁定子句会影响该语句中使用的所有表。如果锁定子句被应用于一个视图或子查询,它会影响该视图或子查询中使用的所有表。不过,这些子句不适用于被主查询引用的 WITH 查询。如果你希望在一个 WITH 查询内部发生行锁定,请在该 WITH 查询中指定一个锁定子句。
如果需要为不同的表指定不同的锁定行为,可以书写多个锁定子句。如果同一个表被多于一个锁定子句提及(或被隐式影响),则它的处理方式就如同只被其中最强的那个指定了一样。类似地,如果在影响它的任何一个子句中指定了 NOWAIT,该表就会按照这一选项处理。否则,如果在影响它的任何一个子句中指定了 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;
该代码将导致 FOR UPDATE 锁未能在 ROLLBACK TO 之后被保存下来。这个问题已在 9.3 版本中被修复。
一个 SELECT 命令如果在 READ COMMITTED 事务隔离级别下运行,并且使用了 ORDER BY 和锁定子句,就可能乱序返回行。这是因为 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,再后面是查询的递归部分。务必确保查询的递归部分最终将不返回任何元组,否则该查询将无限循环。(更多示例请参见 Section 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。但对于输出列名来说,由于存在语法歧义,这样做并不实用。
SELECT 子查询在 FROM 中的别名根据 SQL 标准,SELECT 子查询在 FROM 列表中必须有一个别名。在 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(...) aliasFROM LATERAL (SELECT 。注意,这里的 func(...)) aliasLATERAL 被认为是隐式的;这是因为标准要求 LATERAL 语义:一个 UNNEST() 项处于 FROM 中时即适用该语义。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 Clause 所示。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 标准的一个扩展。
MATERIALIZED 和 NOT MATERIALIZED 这两个 WITH 选项是对 SQL 标准的扩展。