受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

SELECT

SELECT, TABLE, WITH — 从表或视图中检索行

Synopsis

[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
    [ { * | expression [ [ AS ] output_name ] } [, ...] ]
    [ FROM from_item [, ...] ]
    [ WHERE condition ]
    [ GROUP BY [ ALL | DISTINCT ] grouping_element [, ...] ]
    [ HAVING condition ]
    [ WINDOW window_name AS ( window_definition ) [, ...] ]
    [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
    [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
    [ LIMIT { count | ALL } ]
    [ OFFSET start [ ROW | ROWS ] ]
    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]
    [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF from_reference [, ...] ] [ 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 join_type from_item { ON join_condition | USING ( join_column [, ...] ) [ AS join_using_alias ] }
    from_item NATURAL join_type from_item
    from_item CROSS JOIN from_item

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 | merge )
        [ SEARCH { BREADTH | DEPTH } FIRST BY column_name [, ...] SET search_seq_col_name ]
        [ CYCLE column_name [, ...] SET cycle_mark_col_name [ TO cycle_mark_value DEFAULT cycle_mark_default ] USING cycle_path_col_name ]

TABLE [ ONLY ] table_name [ * ]

描述

SELECT检索零个或多个表中的行。 SELECT的一般处理如下:

  1. WITH列表中的所有查询都会被计算。 这些实际上充当临时表,可以在FROM列表中引用。 在FROM列表中多次引用的WITH查询只会计算一次, 除非使用NOT MATERIALIZED进行其他指定。 (参见下面的WITH Clause。)

  2. 所有FROM列表中的元素都会被计算。 (FROM列表中的每个元素都是一个真实或虚拟表。) 如果在FROM列表中指定了多个元素,则它们会被交叉连接在一起。 (参见下面的FROM Clause。)

  3. 如果指定了WHERE子句,则不满足条件的所有行将从输出中删除。 (请参见下面的WHERE Clause。)

  4. 如果指定了GROUP BY子句, 或者存在聚合函数调用, 输出将被组合成在一个或多个值上匹配的行组, 并计算聚合函数的结果。 如果存在HAVING子句, 它将消除不满足给定条件的组。(参见 GROUP BY ClauseHAVING Clause。) 虽然查询输出列名义上是在下一步计算的, 但它们也可以在GROUP BY子句中被引用(按名称或序号)。

  5. 实际输出行是使用每个选定行或行组的SELECT输出表达式计算的。 (参见下面的SELECT List。)

  6. SELECT DISTINCT消除结果中的重复行。 SELECT DISTINCT ON会消除在所有指定表达式上匹配的行,只保留每组中的第一行。 SELECT ALL(默认)将返回所有候选行,包括重复行。 (参见下面的DISTINCT Clause。)

  7. 使用操作符UNIONINTERSECTEXCEPT, 可以将多个SELECT语句的输出合并成一个结果集。 UNION操作符返回在一个或两个结果集中的所有行。 INTERSECT操作符返回同时出现在两个结果集中的所有行。 EXCEPT操作符返回在第一个结果集中但不在第二个结果集中的行。 在这三种情况下,除非指定ALL,否则将消除重复行。 还可以添加噪声词DISTINCT,以明确指定去重。 请注意,这里的默认行为是DISTINCT,即使SELECT本身的默认行为是ALL。 (请参见下面的UNION ClauseINTERSECT ClauseEXCEPT Clause。)

  8. 如果指定了ORDER BY子句,则返回的行按指定顺序排序。 如果没有给出ORDER BY,则按系统认为最快的顺序返回行。 (参见下面的ORDER BY Clause。)

  9. 如果指定了LIMIT(或FETCH FIRST)或OFFSET子句, SELECT语句只返回结果行的子集。(参见下面的LIMIT Clause。)

  10. 如果指定了FOR UPDATEFOR NO KEY UPDATEFOR SHAREFOR KEY SHARESELECT语句将选定的行锁定,防止并发更新。(参见下面的The Locking Clause。)

你必须拥有SELECT命令中使用到的每一列上的 SELECT权限。FOR NO KEY UPDATEFOR UPDATEFOR SHARE或者FOR KEY SHARE 还要求具备UPDATE权限(对这样选中的每个表至少一列)。

参数

WITH 子句

WITH子句允许你指定一个或多个可在主查询中按名称 引用的子查询。这些子查询在主查询执行期间实际上充当临时表或 视图。每个子查询都可以是SELECTTABLEVALUESINSERTUPDATEDELETE或者 MERGE语句。在WITH中编写 数据修改语句(INSERTUPDATEDELETE或者 MERGE)时,通常要包括一个 RETURNING子句。被主查询读取并构成临时表的是 RETURNING的输出,而不是该语句所修改的 底层表。如果省略RETURNING,该语句仍会执行,但不会 产生输出,因此主查询无法把它当作表来引用。

对于每个WITH查询,都必须指定一个名称(不带模式限定)。 还可以指定一个列名列表;如果省略,则列名将从子查询中推导出来。

如果指定了RECURSIVE,则允许一个 SELECT子查询使用名称引用自身。 这样一个子查询的形式必须是

non_recursive_term UNION [ ALL | DISTINCT ] recursive_term

其中递归自引用必须出现在UNION的右手边。每个 查询中只允许一个递归自引用。不支持递归数据修改语句,但是 可以在一个数据查询语句中使用一个递归 SELECT查询的结果。示例可见 Section 7.8

RECURSIVE的另一个效果是 WITH查询不需要被排序:一个查询可以引用另一个 在列表中比它靠后的查询(不过,循环引用或者互递归没有实现)。 如果没有RECURSIVEWITH 查询只能引用在WITH列表中位置更前面的兄弟 WITH查询。

WITH子句中有多个查询时,RECURSIVE应只编写一次,紧跟在WITH之后。 它适用于WITH子句中的所有查询,尽管它对不使用递归或前向引用的查询没有影响。

可选的SEARCH子句会计算一个搜索序列列 (search sequence column),可用于按广度优先或深度优先 顺序对递归查询结果进行排序。给出的列名列表指定了用于跟踪已访问行 的行键。名为search_seq_col_name的列会被添 加到WITH查询的结果列列表中。可以在外层查询中按该列 排序,以获得相应的顺序。示例请见Section 7.8.2.1

可选的CYCLE子句用于检测递归查询中的环路。给出的列名 列表指定了用于跟踪已访问行的行键。名为 cycle_mark_col_name的列会被添加到 WITH查询的结果列列表中。检测到环路时,该列会被设为 cycle_mark_value,否则设为 cycle_mark_default。此外,一旦检测到环路, 递归联合的处理就会停止。cycle_mark_valuecycle_mark_default必须是常量,并且必须能够 强制转换为某个公共数据类型,而且该数据类型必须具有不等操作符。 (SQL 标准要求它们是布尔常量或字符串,但 PostgreSQL 不作此要求。) 默认使用TRUEFALSE(类型为 boolean)。此外,名为 cycle_path_col_name的列也会被添加到 WITH查询的结果列列表中。该列在内部用于跟踪已访问的 行。示例请见Section 7.8.2.2

SEARCHCYCLE子句只对递归 WITH查询有效。with_query 必须是两个SELECT(或等效)命令的 UNION(或UNION ALL),且不允许嵌套 UNION。如果同时使用这两个子句, SEARCH子句添加的列会出现在CYCLE 子句添加的列之前。

主查询和WITH查询(概念上)都在同一时间执行。 这意味着,除了读取其RETURNING输出之外,查询的其 他部分都看不到WITH中数据修改语句的效果。如果两个这 样的数据修改语句试图修改同一行,结果未指定。

WITH查询的一个关键属性是,即使主查询多次引用它们, 它们通常在主查询的每次执行中也只会计算一次。特别是,数据修改语句 保证执行一次且仅执行一次,而不管主查询是否读取了它们的全部输出或 其中任何部分。

不过,可以将WITH查询标记为 NOT MATERIALIZED,从而取消这一保证。在这种情况下, WITH查询可以折叠进主查询,就像它是主查询 FROM子句中的一个简单子SELECT一样。 如果主查询多次引用该WITH查询,这会导致重复计算;但如果 每次使用只需要WITH查询总输出中的少量行, NOT MATERIALIZED仍可能因为允许联合优化而带来净收益。 如果NOT MATERIALIZED附加在递归的WITH查询上, 或附加在非无副作用的WITH查询上(也就是说,不是 不包含可变函数的普通SELECT),则会被忽略。

默认情况下,如果某个无副作用的WITH查询在主查询的 FROM子句中恰好只使用一次,它就会被折叠进主查询。 这使得在语义上不可见的情况下,查询的两个层级可以联合优化。不过, 也可以把WITH查询标记为MATERIALIZED, 以阻止这种折叠。例如,当WITH查询被用作优化围栏, 以防止规划器选择糟糕的计划时,这么做就很有用。 PostgreSQL 在 v12 之前从不进行这种折叠, 因此为旧版本编写的查询可能依赖WITH来充当优化围栏。

更多信息请见Section 7.8

FROM 子句

FROM子句为SELECT 指定一个或者更多源表。如果指定了多个源表,结果将是所有源表的 笛卡尔积(交叉连接)。但是通常会增加限定条件(通过 WHERE)来把返回的行限制为该笛卡尔积的一个小子集。

FROM子句可以包含以下元素:

table_name #

要扫描的现有表或视图的名称(可选模式限定符)。如果在表名之前指定ONLY, 则仅扫描该表。如果未指定ONLY,则扫描该表及其所有后代表(如果有)。 可选地,可以在表名后指定*,以明确指示包括后代表。

alias #

包含该别名的FROM项的替代名称。别名可用于简写, 或者消除自连接(同一张表被扫描多次)中的歧义。提供别名后,它会 完全隐藏表或函数的实际名称;例如给定FROM foo AS fSELECT的其余部分必须把这个FROM 项写成f而不是foo。如果写了别名, 还可以写列别名列表,为该表的一个或多个列提供替代名称。

TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] #

跟在table_name之后的 TABLESAMPLE子句表示,应使用指定的 sampling_method从该表中 取回行的一个子集。这种抽样先于任何其他过滤条件(例如 WHERE子句)的应用。标准 PostgreSQL发行版包含两种抽样方法, 即BERNOULLISYSTEM;其他抽样 方法可以通过扩展安装到数据库中。

BERNOULLISYSTEM抽样方法 各接受一个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

对于INNEROUTER连接类型,必须指定连接条件,即下列三者之一: ON join_conditionUSING (join_column [, ...]), 或者NATURAL。请参见下文的含义。

一个JOIN子句会把两个FROM项组合起来,为方便起见,我们把它们称为, 尽管实际上它们可以是任何类型的FROM项。 如有必要,可以使用括号来确定嵌套顺序。 在没有括号时,JOIN按从左到右的顺序嵌套。 无论如何,JOIN的结合都比分隔FROM列表项的逗号更紧密。 所有JOIN选项都只是记法上的便利,因为它们并没有提供任何无法用普通FROMWHERE做到的能力。

LEFT OUTER JOIN返回已限定笛卡尔积中的所有行(即,通过其连接条件的所有组合行),外加左侧表中每一行的一个副本;对于这些行,不存在通过连接条件的右侧行。这样的左侧行会通过在右侧列中插入空值而扩展到连接表的完整宽度。注意,在决定哪些行有匹配项时,只考虑JOIN子句自身的条件;外层条件是在之后应用的。

相反,RIGHT OUTER JOIN返回所有连接后的行,再加上每个未匹配右侧行对应的一行 (左侧用空值扩展)。这只是一种记法上的便利,因为你可以通过交换左右表 将其改写成LEFT OUTER JOIN

FULL OUTER JOIN返回所有连接后的行,再加上每个未匹配的左侧行(右侧用空值扩展),以及每个未匹配的右侧行(左侧用空值扩展)。

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项。得到的行随后照常与生成它们的那些行 连接。这个过程会针对列源表中的每一行或每一组行重复执行。

列源表必须通过INNERLEFT连接到 LATERAL项,否则就无法得到一个定义良好的行集合,用来 计算该LATERAL项的每一组结果行。因此,尽管像 X RIGHT JOIN LATERAL Y 这样的结构在语法上有效,但实际上并不允许Y 引用X

WHERE 子句

可选的WHERE子句的形式

WHERE condition

其中condition 是任一计算得到boolean类型结果的表达式。任何不满足 这个条件的行都会从输出中被消除。如果用一行的实际值替换其中的 变量引用后,该表达式返回真,则该行符合条件。

GROUP BY 子句

可选的GROUP BY子句的形式

GROUP BY [ ALL | DISTINCT ] grouping_element [, ...]

GROUP BY会把所有在分组表达式上具有相同值的已选中 行压缩成单独一行。用于 grouping_element中的 expression可以是输入列名、输出列 (SELECT列表项)的名称或序号或者由输入列 值构成的任意表达式。在出现歧义时,GROUP BY名称 将被解释为输入列名而不是输出列名。

如果GROUPING SETSROLLUPCUBE中的任何一个作为分组元素出现,那么整个 GROUP BY子句就定义了若干个相互独立的 分组集。其效果等价于在多个子查询之间构造一 个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 UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能和 GROUP BY一起指定。

HAVING 子句

可选的HAVING子句的形式

HAVING condition

其中conditionWHERE子句中指定的条件相同。

HAVING消除不满足该条件的分组行。 HAVINGWHERE不同: WHERE会在应用GROUP BY之前过滤个体行,而HAVING过滤由 GROUP BY创建的分组行。 condition中引用 的每一列都必须无歧义地引用某个分组列,除非该引用出现在聚合 函数中,或者该非分组列函数依赖于分组列。

即使没有GROUP BY子句,HAVING 的存在也会把一个查询转变成一个分组查询。这和查询中包含聚合函数但没有 GROUP BY子句时的情况相同。所有被选择的行都被认为是一个 单一分组,并且SELECT列表和 HAVING子句只能从聚合函数内部引用表列。如果该 HAVING条件为真,这样的查询将输出单独一行; 否则不返回行。

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能与 HAVING一起指定。

WINDOW 子句

可选的WINDOW子句的形式

WINDOW window_name AS ( window_definition ) [, ...]

其中window_name 是一个可以从OVER子句或者后续窗口定义中引用的名称。 window_definition

[ existing_window_name ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ frame_clause ]

如果指定了一个existing_window_name, 它必须引用WINDOW列表中一个更早出现的项。新窗口将从 该项中复制它的分区子句,以及排序子句(如果有)。在这种情况下,新窗口 不能指定它自己的PARTITION BY子句,并且只有在被复制 的窗口没有ORDER BY子句时,才可以指定 ORDER BY。新窗口总是使用自己的帧子句,而被复制的 窗口不得指定帧子句。

PARTITION BY列表元素的解释以 GROUP BY子句元素的方式 进行,不过它们总是简单表达式并且绝不能是输出列的名称或编号。另一个区 别是这些表达式可以包含聚合函数调用,而这在常规GROUP BY 子句中是不被允许的。它们被允许的原因是窗口是出现在分组和聚合之后的。

类似地,ORDER BY列表元素的解释也以语句级 ORDER BY子句元素的方式进行, 不过该表达式总是被当做简单表达式并且绝不会是输出列的名称或编号。

可选的frame_clause为依赖于帧 的窗口函数定义窗口帧(并非所有窗口函数都依赖于帧)。 对于查询中的每一行(称为当前行),窗口帧都是一组相关行。 frame_clause可以是

{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion ]
{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ]

之一,其中frame_startframe_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 FOLLOWINGframe_end不能是UNBOUNDED PRECEDING, 并且在上述frame_startframe_end选项列表中, 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表示帧到分区的最后一行结束, 与使用RANGEROWS还是GROUPS 模式无关。在ROWS模式中,CURRENT ROW表示 帧从当前行开始或在当前行结束;而在RANGEGROUPS模式中,它表示帧从当前行在 ORDER BY排序中的第一个平级行开始,或在最后一个平级行结束。 offset PRECEDINGoffset FOLLOWING选项的含义会随帧 模式而变化。在ROWS模式中, offset是一个整数,表示帧从当前行之前或之后多少行 开始或结束。在GROUPS模式中, offset是一个整数,表示帧从当前行所在平级组之前或 之后多少个平级组开始或结束,其中平级组是按窗口 ORDER BY子句判断等价的一组行。在RANGE 模式中,使用offset选项要求窗口定义中恰好有一个 ORDER BY列。此时,帧包含的行满足:其排序列值与当前行 的排序列值相比,至多小于offset(对于 PRECEDING)或至多大于offset (对于FOLLOWING)。在这些情况下, 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 UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能和 WINDOW一起被指定。

窗口函数的详细描述在 Section 3.5Section 4.2.8以及 Section 7.2.5中。

SELECT 列表

SELECT列表(位于关键词 SELECTFROM之间)指定构成 SELECT语句输出行的表达式。这些表达式 可以(并且通常确实会)引用FROM子句中计算得到的列。

正如在表中一样,SELECT的每一个输出列都有一个名称。 在一个简单的SELECT中,这个名称只是被用来标记要显 示的列,但是当SELECT是一个大型查询的一个子查询时,大型查询 会把该名称看做子查询产生的虚表的列名。要指定用于输出列的名称,在该列的表达式 后面写上 AS output_name( 你可以省略AS,但只能在期望的输出名称不匹配任何 PostgreSQL关键词(见Appendix C)时省略。为了避免和未来增加的关键词冲突, 推荐总是写上AS或者用双引号引用输出名称)。如果你不指定列名, PostgreSQL会自动选择一个名称。如果列的表达式 是一个简单的列引用,那么被选择的名称就和该列的名称相同。在使用函数或者类型名称 的更复杂的情况中,系统可能会生成诸如 ?column?之类的名称。

一个输出列的名称可以被用来在ORDER BY以及 GROUP BY子句中引用该列的值,但是不能用于 WHEREHAVING子句(在其中 必须写出表达式)。

可以在输出列表中写*来取代表达式,它是被选中 行的所有列的一种简写方式。还可以写 table_name.*,它 是只来自那个表的所有列的简写形式。在这些情况中无法用 AS指定新的名称,输出行的名称将和表列的名称相同。

按照 SQL 标准,输出列表中的表达式应当在应用DISTINCTORDER BYLIMIT之前计算。对于 DISTINCT来说,这显然是必要的,否则就不清楚究竟要对哪 些值去重。不过,在很多情况下,如果先执行ORDER BYLIMIT再计算输出表达式会更方便,特别是当输出列表中包含 可变函数或代价高昂的函数时。那样一来,函数求值的顺序更符合直觉,也不 会去计算那些根本不会出现在输出中的行。只要输出表达式没有被 DISTINCTORDER BYGROUP BY引用,PostgreSQL 实际上就会在排序和限制行数之后再计算它们。(反例是 SELECT f(x) FROM tab ORDER BY 1,它显然必须在排序前 计算f(x)。)包含集合返回函数的输出表达式则实际上会在 排序之后、限制之前计算,这样LIMIT才能截断该集合返回函数 产生的输出。

Note

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 UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能和 DISTINCT一起使用。

UNION 子句

UNION子句的一般形式如下:

select_statement UNION [ ALL | DISTINCT ] select_statement

select_statement 是任何没有ORDER BYLIMITFOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE子句的 SELECT语句。(如果某个子表达式被圆括号括起来, ORDER BYLIMIT可以附加在它上面。 如果没有圆括号,这些子句会被视为作用于UNION的结果, 而不是作用于其右侧输入表达式。)

UNION操作符计算相关 SELECT语句所返回的行的并集。如果一行 至少出现在两个结果集中的一个内,它就会在并集中。作为 UNION两个操作数的 SELECT语句必须产生相同数量的列并且 对应位置上的列必须具有兼容的数据类型。

UNION的结果不会包含重复行,除非指定了 ALL选项。ALL会阻止消除重复(因此, UNION ALL通常显著地快于UNION, 尽量使用ALL)。也可以写上DISTINCT, 以显式指定默认的去重行为。

除非用圆括号指定计算顺序, 同一个SELECT语句中的多个 UNION操作符会从左至右计算。

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能用于UNION结果或者 UNION的任何输入。

INTERSECT 子句

INTERSECT子句的一般形式如下:

select_statement INTERSECT [ ALL | DISTINCT ] select_statement

select_statement 是任何没有ORDER BYLIMITFOR NO KEY UPDATEFOR UPDATEFOR 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 UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能用于INTERSECT结果或者 INTERSECT的任何输入。

EXCEPT 子句

EXCEPT子句的一般形式如下:

select_statement EXCEPT [ ALL | DISTINCT ] select_statement

select_statement 是任何没有ORDER BYLIMITFOR NO KEY UPDATEFOR UPDATEFOR SHARE以及FOR KEY SHARE子句的 SELECT语句。

EXCEPT操作符计算位于左侧 SELECT语句的结果中但不在右 SELECT语句结果中的行集合。

EXCEPT的结果不会包含重复行,除非指定了 ALL选项。如果有ALL,一个在左表中有 m次重复并且在右表中有 n次重复的行将会在结果集中出现 max(m-n,0) 次。 也可以写上DISTINCT,以显式指定默认的去重行为。

除非用圆括号指定计算顺序, 同一个SELECT语句中的多个 EXCEPT操作符会从左至右计算。 EXCEPT的优先级与 UNION相同。

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR 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;

这种特性的一个限制是一个应用在UNIONINTERSECTEXCEPT子句结果上的 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 DESCORDER BY x DESC, y DESC是不同的。

字符串数据会被根据引用到被排序列上的排序规则排序。根据需要可以通过在 expression中包括一个 COLLATE子句来覆盖,例如 ORDER BY mycolumn COLLATE "en_US"。更多信息请见 Section 4.2.10Section 23.2

LIMIT 子句

LIMIT子句由两个独立的子句构成:

LIMIT { count | ALL }
OFFSET start

参数count指定要返回 的最大行数,而start 指定在返回行之前要跳过的行数。在两者都被指定时,在开始计算要返回的 count行之前会跳过 start行。

如果count表达式计算 为 NULL,它会被当成LIMIT ALL,即没有限制。如果 start计算为 NULL,它会被当作OFFSET 0

SQL:2008 引入了一种不同的语法来达到相同的结果, PostgreSQL也支持它:

OFFSET start { ROW | ROWS }
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }

在这种语法中,标准要求startcount是字面常量、参数或变量名。 作为PostgreSQL扩展,也允许使用其他表达式, 但通常需要用圆括号括起来以避免歧义。 如果在一个FETCH子句中省略count,它的默认值为 1。 WITH TIES选项用于返回结果集中根据ORDER BY子句与最后一行并列的任何额外行;这种情况下必须指定ORDER BY,并且不允许使用SKIP LOCKEDROWROWS以及FIRSTNEXT都是噪声词,不影响这些子句的效果。 根据标准,如果都存在,OFFSET子句必须出现在FETCH子句之前。 但是PostgreSQL更宽松,它允许两种顺序。

在使用LIMIT时,用一个ORDER BY子句把 结果行约束到一个唯一顺序是个好办法。否则你将得到该查询结果行的 一个不可预测的子集 — 你可能要求从第 10 到第 20 行,但是在 什么顺序下的第 10 到第 20 呢?除非指定ORDER BY,你 是不知道顺序的。

查询规划器在生成一个查询计划时会考虑LIMIT,因此 根据你使用的LIMITOFFSET,你很可能 得到不同的计划(得到不同的行序)。所以,使用不同的 LIMIT/OFFSET值来选择一个查询结果的 不同子集将会给出不一致的结果,除非你 用ORDER BY强制一种可预测的结果顺序。这不是一个 缺陷,它是 SQL 不承诺以任何特定顺序(除非使用 ORDER BY来约束顺序)给出一个查询结果这一事实造 成的必然后果。

如果没有一个ORDER BY来强制选择一个确定的子集, 重复执行同样的LIMIT查询甚至可能会返回一个表中行 的不同子集。同样,这也不是一种缺陷,在这种情况下也无法 保证结果的确定性。

锁定子句

FOR UPDATEFOR NO KEY UPDATEFOR SHAREFOR KEY SHARE锁定子句,它们影响SELECT 把行从表中取得时如何对它们加锁。

锁定子句的一般形式:

FOR lock_strength [ OF from_reference [, ...] ] [ NOWAIT | SKIP LOCKED ]

其中lock_strength可以是

UPDATE
NO KEY UPDATE
SHARE
KEY SHARE

from_reference必须是 FROM 子句中被引用的表alias 或非隐藏的table_name。 更多关于每一种行级锁模式的信息可见Section 13.3.2

为了防止该操作等待其他事务提交,可使用NOWAITSKIP LOCKED选项。使用NOWAIT时,如果 选中的行不能被立即锁定,该语句会直接报错而不是等待。使用 SKIP LOCKED时,任何无法立即锁定的已选中行都会被跳过。 跳过已锁定行会提供数据的不一致视图,因此不适合一般用途的工作,但可用 于避免多个消费者访问类似队列表时的锁竞争。注意, NOWAITSKIP LOCKED只适用于行级锁; 所需的ROW SHARE表级锁仍会按常规方式取得(见Chapter 13)。 如果想要不等待的表级锁,你可以先使用带NOWAITLOCK

如果在一个锁定子句中提到了特定的表,则只有来自于那些表的 行会被锁定,任何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 版本中修复。

Caution

一个运行在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 BYLIMIT或者其他 限制组合使用时。因此只有在并发更新排序列并且要求严格的排序结果时才推 荐使用这种技术。

REPEATABLE READSERIALIZABLE事务隔离级别下, 这将导致序列化失败(带有SQLSTATE'40001'), 因此在这些隔离级别下不可能接收到无序的行。

TABLE 命令

命令

TABLE name

等价于

SELECT * FROM name

它可以作为顶层命令使用,也可以作为复杂查询中一种节省空间的语法变体。只有 WITHUNIONINTERSECTEXCEPTORDER BYLIMITOFFSETFETCH以及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

接下来的示例展示了如何得到表distributorsactors的并集,把结果限制为那些在每个表中以 字母 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)。

这个示例使用LATERALmanufacturers 表的每一行应用一个集合返回函数get_product_names()

SELECT m.name AS mname, pname
FROM manufacturers m, LATERAL get_product_names(m.id) pname;

当前没有任何产品的制造商不会出现在结果中,因为这是一个内连接。 如果我们希望把这类制造商的名称包括在结果中,我们可以:

SELECT m.name AS mname, pname
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true;

兼容性

当然,SELECT语句与 SQL 标准兼容。 但它也有一些扩展和缺失的特性。

省略的FROM子句

PostgreSQL允许我们省略 FROM子句。一种简单的使用是计算简单表达式 的结果:

SELECT 2+2;

 ?column?
----------
        4

某些其他SQL数据库需要引入一个假的 单行表放在该SELECTFROM子句中才能做到这一点。

SELECT列表

SELECT之后的输出表达式列表可以为空, 这会产生一个零列的结果表。按照 SQL 标准,这不是合法的 语法。PostgreSQL允许 这样做,是为了与允许零列表保持一致。不过在使用 DISTINCT时不允许空列表。

省略AS关键词

在 SQL 标准中,只要新列名是一个合法的列名(就是说与任何保留关键词不同), 就可以省略输出列名之前的可选关键词ASPostgreSQL要稍微严格些:只要新列名匹配 任何关键词(保留或者非保留)就需要AS。推荐的习惯是使用 AS或者带双引号的输出列名来防止与未来增加的关键词可能的冲突。

FROM项中,标准和 PostgreSQL都允许在非保留关键字别名前 省略AS。但是由于语法歧义,这种写法无法 用于输出列名。

FROM 中省略子 SELECT 别名

根据 SQL 标准,FROM 列表中的子 SELECT 必须有别名。 在 PostgreSQL 中,这个别名可以省略。

ONLY和继承

在书写ONLY时,SQL 标准要求在表名周围加上圆括号,例如 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 BYORDER BY可用的名字空间

在 SQL-92 标准中,一个ORDER BY子句只能使用输出 列名或者序号,而一个GROUP BY子句只能使用基于输 入列名的表达式。PostgreSQL扩展了 这两种子句以允许它们使用其他的选择(但如果有歧义时还是使用标准的 解释)。PostgreSQL也允许两种子句 指定任意表达式。注意出现在一个表达式中的名称将总是被当做输入列名而 不是输出列名。

SQL:1999 及其后的标准使用了一种略微不同的定义,它并不完全向后兼容 SQL-92。不过,在大部分的情况下, PostgreSQL会以与 SQL:1999 相同的 方式解释ORDER BYGROUP BY表达式。

函数依赖

只有当一个表的主键被包括在GROUP BY列表中时, PostgreSQL才识别函数依赖(允许 从GROUP BY中省略列)。SQL 标准指定了应该要识别 的额外情况。

LIMITOFFSET

LIMITOFFSET子句是 PostgreSQL-特有的语法,在 MySQL也被使用。SQL:2008 标准已经 引入了具有相同功能的子句OFFSET ... FETCH {FIRST|NEXT} ...(如上文 LIMIT Clause中所示)。这种语法 也被IBM DB2使用( Oracle编写的应用常常使用自动生成的 rownum列来实现这些子句的效果,这在 PostgreSQL 中是没有的)。

FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE

尽管 SQL 标准中出现了FOR UPDATE,但标准只允许它作为 DECLARE CURSOR的一个选项。 PostgreSQL允许它出现在任何 SELECT查询以及子SELECT中,但这是 一种扩展。FOR NO KEY UPDATEFOR SHARE 以及FOR KEY SHARE变体以及NOWAITSKIP LOCKED选项没有在标准中出现。

WITH中的数据修改语句

PostgreSQL允许将INSERTUPDATEDELETE以及MERGE用作WITH 查询。这在 SQL 标准中是找不到的。

非标准子句

DISTINCT ON ( ... )是 SQL 标准的扩展。

ROWS FROM( ... )是 SQL 标准的扩展。

WITHMATERIALIZEDNOT MATERIALIZED选项是 SQL 标准的扩展。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。