2024年9月26日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

SELECT

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

概要

[ 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 ] [...] ]

where from_item can be one of:

    [ 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

and grouping_element can be one of:

    ( )
    expression
    ( expression [, ...] )
    ROLLUP ( { expression | ( expression [, ...] ) } [, ...] )
    CUBE ( { expression | ( expression [, ...] ) } [, ...] )
    GROUPING SETS ( grouping_element [, ...] )

and with_query is:

    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 列表中引用。除非使用 NOT MATERIALIZED 指定其他情况,否则在 FROM 中多次引用的 WITH 查询仅计算一次。(请参阅下面的 WITH 子句。)

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

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

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

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

  6. SELECT DISTINCT 从结果中排除重复行。SELECT DISTINCT ON 排除所有指定表达式匹配的行。SELECT ALL(默认值)将返回所有候选行,包括重复行。(请参阅下面的 DISTINCT 子句。)

  7. 使用运算符 UNIONINTERSECTEXCEPT,可以组合多个 SELECT 语句的输出以形成单个结果集。UNION 运算符返回结果集中一个或两个结果集中存在的所有行。INTERSECT 运算符返回严格存在于两个结果集中的所有行。EXCEPT 运算符返回第一个结果集中存在但在第二个结果集中不存在的行。在这三种情况下,除非指定了 ALL,否则重复行将被排除。可以添加噪音词 DISTINCT 以明确指定排除重复行。请注意,即使 ALLSELECT 本身的默认行为,但此处 DISTINCT 也是默认行为。(请参阅下面的 UNION 子句INTERSECT 子句EXCEPT 子句。)

  8. 如果指定了 ORDER BY 子句,则返回的行将按指定的顺序排序。如果没有给出 ORDER BY,则行将按系统找到的最快的顺序返回。(请参阅下面的 ORDER BY 子句。)

  9. 如果指定了 LIMIT(或 FETCH FIRST)或 OFFSET 子句,则 SELECT 语句仅返回结果行的一个子集。(请参阅下面的 LIMIT 子句。)

  10. 如果指定了 FOR UPDATEFOR NO KEY UPDATEFOR SHAREFOR KEY SHARE,则 SELECT 语句会锁定选定的行以防止并发更新。(请参阅下面的 锁定子句。)

您必须对 SELECT 命令中使用的每个列具有 SELECT 权限。使用 FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE 还需要 UPDATE 权限(至少对于每个这样选定的表的其中一列)。

参数

WITH 子句

WITH 子句允许您指定一个或多个子查询,这些子查询可以在主查询中按名称引用。子查询在主查询持续期间有效地充当临时表或视图。每个子查询可以是 SELECTTABLEVALUESINSERTUPDATEDELETEMERGE 语句。在 WITH 中编写数据修改语句(INSERTUPDATEDELETEMERGE)时,通常会包含 RETURNING 子句。正是 RETURNING 的输出(而不是语句修改的基础表)形成了主查询读取的临时表。如果省略了 RETURNING,则语句仍将执行,但它不会产生任何输出,因此主查询无法将其引用为表。

必须为每个 WITH 查询指定一个名称(不带模式限定)。可以选择指定列名列表;如果省略了此列表,则列名将从子查询推断。

如果指定了 RECURSIVE,则它允许 SELECT 子查询按名称引用自身。此类子查询必须具有以下形式:

non_recursive_term UNION [ ALL | DISTINCT ] recursive_term

其中递归自引用必须出现在 UNION 的右侧。每个查询只允许一个递归自引用。不支持递归数据修改语句,但您可以在数据修改语句中使用递归 SELECT 查询的结果。请参阅 第 7.8 节 以了解示例。

RECURSIVE 的另一个影响是 WITH 查询不需要排序:一个查询可以引用列表中后面的另一个查询。(但是,循环引用或相互递归尚未实现。)如果没有 RECURSIVE,则 WITH 查询只能引用 WITH 列表中较早的同级 WITH 查询。

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

可选的 SEARCH 子句计算一个搜索序列列,该列可用于以广度优先或深度优先顺序对递归查询的结果进行排序。提供的列名列表指定将用于跟踪已访问行的行键。名为search_seq_col_name的列将添加到 WITH 查询的结果列列表中。可以在外部查询中按此列排序以实现相应的排序。请参阅 第 7.8.2.1 节 以了解示例。

可选的 CYCLE 子句用于检测递归查询中的循环。提供的列名列表指定将用于跟踪已访问行的行键。名为cycle_mark_col_name的列将添加到 WITH 查询的结果列列表中。当检测到循环时,此列将设置为cycle_mark_value,否则设置为cycle_mark_default。此外,当检测到循环时,递归联合的处理将停止。cycle_mark_valuecycle_mark_default必须是常量,并且必须可以强制转换为公共数据类型,并且数据类型必须具有不等式运算符。(SQL 标准要求它们是布尔常量或字符字符串,但 PostgreSQL 没有此要求。)默认情况下,使用 TRUEFALSE(类型为 boolean)。此外,名为cycle_path_col_name的列将添加到 WITH 查询的结果列列表中。此列在内部用于跟踪已访问的行。请参阅 第 7.8.2.2 节 以了解示例。

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

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

WITH 查询的一个关键特性是,它们通常在主查询执行期间仅评估一次,即使主查询多次引用它们也是如此。特别是,数据修改语句保证只执行一次,无论主查询是否读取其所有或任何输出。

但是,可以将 WITH 查询标记为 NOT MATERIALIZED 以删除此保证。在这种情况下, WITH 查询可以折叠到主查询中,就像它是主查询 FROM 子句中的一个简单的子 SELECT 一样。如果主查询多次引用该 WITH 查询,这会导致重复计算;但是,如果每次使用仅需要 WITH 查询总输出的几行,则 NOT MATERIALIZED 可以通过允许联合优化查询来提供净节省。如果它附加到递归或非副作用免费(即,不是不包含易失函数的简单 SELECT)的 WITH 查询,则忽略 NOT MATERIALIZED

默认情况下,如果无副作用的 WITH 查询在主查询的 FROM 子句中恰好使用一次,则将其折叠到主查询中。这允许在语义上应该不可见的情况下联合优化这两个查询级别。但是,可以通过将 WITH 查询标记为 MATERIALIZED 来阻止这种折叠。例如,如果 WITH 查询用作优化栅栏以防止计划程序选择错误的计划,这可能很有用。v12 之前的 PostgreSQL 版本从未进行过此类折叠,因此为旧版本编写的查询可能会依赖 WITH 充当优化栅栏。

有关更多信息,请参见第 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

NATURALUSING 列表的简写,该列表提到了两个表中所有具有匹配名称的列。如果没有公共列名,则 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 项目。生成的 rows(s) 与它们计算出的 rows 通常一样连接。对于来自列源表(或表)的每一行或行集,重复此过程。

列源表(或表)必须与 LATERAL 项目进行 INNERLEFT 连接,否则将没有一个定义明确的行集来计算 LATERAL 项目的每一行集。因此,尽管诸如 X RIGHT JOIN LATERAL Y 的构造在语法上有效,但实际上不允许 Y 引用 X

WHERE 子句

可选的 WHERE 子句具有以下通用形式

WHERE condition

其中 condition 是任何计算结果为 boolean 类型值的表达式。任何不满足此条件的行都将从输出中删除。如果在将实际行值替换为任何变量引用时返回 true,则该行满足该条件。

GROUP BY 子句

可选的 GROUP BY 子句具有以下通用形式

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

GROUP BY 将压缩为一行所有具有分组表达式相同值的选定行。在 grouping_element 中使用的 expression 可以是输入列名,或输出列(SELECT 列表项)的名称或序数,或由输入列值形成的任意表达式。如果出现歧义,则 GROUP BY 名称将被解释为输入列名而不是输出列名。

如果任何 GROUPING SETSROLLUPCUBE 作为分组元素存在,则整个 GROUP BY 子句定义了一些独立的 grouping sets。其效果等效于在子查询之间构建 UNION ALL,其中各个分组集作为其 GROUP BY 子句。可选的 DISTINCT 子句在处理前删除重复的集合;它不会UNION ALL 转换为 UNION DISTINCT。有关处理分组集的更多详细信息,请参阅第 7.2.4 节

如果使用了聚合函数,则会在构成每个组的所有行中计算聚合函数,为每个组生成一个单独的值。(如果存在聚合函数但没有 GROUP BY 子句,则查询将被视为具有一个包含所有选定行的组。)馈送到每个聚合函数的行集可以通过附加 FILTER 子句到聚合函数调用来进一步过滤;有关更多信息,请参阅第 4.2.7 节。当存在 FILTER 子句时,只有匹配它的行包含在该聚合函数的输入中。

当存在 GROUP BY 时,或者存在任何聚合函数时,SELECT 列表表达式不能引用未分组的列,除非在聚合函数中或未分组的列在功能上依赖于分组列,因为否则将有多个可能的返回值要为未分组的列返回。如果分组列(或其子集)是包含未分组列的表的 primary key,则存在功能依赖性。

请记住,所有聚合函数都在评估 HAVING 子句或 SELECT 列表中的任何标量表达式之前进行评估。这意味着,例如,CASE 表达式不能用于跳过聚合函数的评估;请参阅第 4.2.14 节

目前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE 不能与 GROUP BY 一起指定。

HAVING 子句

可选的 HAVING 子句具有以下通用形式

HAVING condition

其中 condition 与为 WHERE 子句指定的相同。

HAVING 消除不满足条件的组行。HAVINGWHERE 不同:WHERE 在应用 GROUP BY 之前过滤单个行,而 HAVING 过滤由 GROUP BY 创建的组行。在 condition 中引用的每个列都必须明确地引用分组列,除非引用出现在聚合函数中或未分组的列在功能上依赖于分组列。

存在 HAVING 会将查询转换为分组查询,即使没有 GROUP BY 子句也是如此。这与查询包含聚合函数但没有 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。新窗口始终使用自己的 frame 子句;复制的窗口不得指定 frame 子句。

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_end 的选择不能出现在上述frame_startframe_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 表示框架以分区的最后一行结束,而不管RANGEROWSGROUPS 模式如何。在ROWS 模式下,CURRENT ROW 表示框架从当前行开始或结束;但在RANGEGROUPS 模式下,它表示框架从当前行的ORDER BY 顺序中的第一个或最后一个同级开始或结束。offset PRECEDINGoffset 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 只是明确指定不排除当前行或其同级的默认行为。

请注意,如果ORDER BY 顺序没有唯一地排序行,则ROWS 模式可能会产生不可预测的结果。RANGEGROUPS 模式旨在确保在ORDER BY 顺序中是同级的行得到相同的处理:给定同级组中的所有行都将包含在框架中或从框架中排除。

WINDOW 子句的目的是指定查询的SELECT 列表ORDER BY 子句中出现的窗口函数的行为。这些函数可以在其OVER 子句中按名称引用WINDOW 子句条目。但是,WINDOW 子句条目不必在任何地方被引用;如果它没有在查询中使用,则会被简单地忽略。可以完全不使用WINDOW 子句来使用窗口函数,因为窗口函数调用可以在其OVER 子句中直接指定其窗口定义。但是,当多个窗口函数需要相同的窗口定义时,WINDOW 子句可以节省输入。

目前,不能将FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHAREWINDOW 一起指定。

窗口函数在第 3.5 节第 4.2.8 节第 7.2.5 节 中进行了详细描述。

SELECT 列表

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

就像在表中一样,SELECT 的每个输出列都有一个名称。在简单的SELECT 中,此名称仅用于标记要显示的列,但当SELECT 是更大查询的子查询时,较大查询会将此名称视为子查询生成的虚拟表的列名。要指定要用于输出列的名称,请在列的表达式后编写AS output_name。(可以省略AS,但前提是所需的输出名称与任何PostgreSQL 关键字不匹配(请参阅附录 C)。为了防止将来可能添加的关键字,建议始终编写AS 或将输出名称用双引号括起来。)如果未指定列名,则PostgreSQL 会自动选择一个名称。如果列的表达式是简单的列引用,则所选名称与该列的名称相同。在更复杂的情况下,可能会使用函数或类型名称,或者系统可能会回退到生成的名称,例如?column?

输出列的名称可用于在ORDER BYGROUP BY 子句中引用该列的值,但不能在WHEREHAVING 子句中使用;在那里必须写出表达式。

输出列表中可以写* 来代替表达式,作为所选行的所有列的简写。此外,可以写table_name.* 作为仅来自该表的列的简写。在这些情况下,无法使用AS 指定新名称;输出列名将与表列名相同。

根据 SQL 标准,输出列表中的表达式应在应用DISTINCTORDER BYLIMIT 之前计算。当使用DISTINCT 时,这显然是必要的,因为否则不清楚哪些值正在被设为唯一。但是,在许多情况下,如果输出表达式在ORDER BYLIMIT 之后计算,会比较方便;尤其是在输出列表包含任何易变或昂贵的函数时。使用此行为,函数求值的顺序更直观,并且不会有对应于从未出现在输出中的行的求值。PostgreSQL 将有效地在排序和限制之后求值输出表达式,只要这些表达式未在DISTINCTORDER BYGROUP 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 组内行的所需优先级。

目前,不能在 DISTINCT 中指定 FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE

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 语句必须产生相同数量的列,并且相应的列必须具有兼容的数据类型。

除非指定了 ALL 选项,否则 UNION 的结果不包含任何重复行。 ALL 阻止消除重复项。(因此,UNION ALL 通常比 UNION 快得多;在可以的情况下使用 ALL。)可以编写 DISTINCT 以显式指定消除重复行的默认行为。

在同一个 SELECT 语句中的多个 UNION 运算符将从左到右进行评估,除非括号另有指示。

目前,不能为 UNION 结果或 UNION 的任何输入指定 FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE

INTERSECT 子句

INTERSECT 子句具有以下通用形式

select_statement INTERSECT [ ALL | DISTINCT ] select_statement

select_statement 是任何不带 ORDER BYLIMITFOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE 子句的 SELECT 语句。

INTERSECT 运算符计算所涉及的 SELECT 语句返回的行集的交集。如果一行出现在两个结果集中,则该行属于两个结果集的交集。

除非指定了 ALL 选项,否则 INTERSECT 的结果不包含任何重复行。使用 ALL 时,在左侧表中有 m 个重复项而在右侧表中有 n 个重复项的行将在结果集中出现 min(m,n) 次。可以编写 DISTINCT 以显式指定消除重复行的默认行为。

在同一个 SELECT 语句中的多个 INTERSECT 运算符将从左到右进行评估,除非括号另有规定。 INTERSECT 的绑定优先级高于 UNION。也就是说,A UNION B INTERSECT C 将被解读为 A UNION (B INTERSECT C)

目前,不能为 INTERSECT 结果或 INTERSECT 的任何输入指定 FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE

EXCEPT 子句

EXCEPT 子句具有以下通用形式

select_statement EXCEPT [ ALL | DISTINCT ] select_statement

select_statement 是任何不带 ORDER BYLIMITFOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE 子句的 SELECT 语句。

EXCEPT 运算符计算左侧 SELECT 语句结果中存在但右侧 SELECT 语句结果中不存在的行集。

除非指定了 ALL 选项,否则 EXCEPT 的结果不包含任何重复行。使用 ALL 时,在左侧表中有 m 个重复项而在右侧表中有 n 个重复项的行将在结果集中出现 max(m-n,0) 次。可以编写 DISTINCT 以显式指定消除重复行的默认行为。

在同一个 SELECT 语句中的多个 EXCEPT 运算符将从左到右进行评估,除非括号另有规定。 EXCEPT 的绑定优先级与 UNION 相同。

目前,不能为 EXCEPT 结果或 EXCEPT 的任何输入指定 FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE

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"。有关更多信息,请参见第 4.2.10 节第 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 来约束顺序。

即使对于相同LIMIT查询的重复执行,也可能返回表中不同子集的行,如果不存在ORDER BY来强制选择确定性的子集。同样,这不是一个错误;在这种情况下,结果的确定性 simply 不保证。

锁定子句

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 子句中引用的表别名或未隐藏的表名。有关每种行级锁定模式的更多信息,请参阅第 13.3.2 节

要防止操作等待其他事务提交,请使用NOWAITSKIP LOCKED 选项之一。使用NOWAIT,如果无法立即锁定选定的行,则语句会报告错误,而不是等待。使用SKIP LOCKED,任何无法立即锁定的选定行都将被跳过。跳过锁定的行会提供不一致的数据视图,因此这不太适合通用工作,但可用于避免多个使用者访问队列式表的锁争用。请注意,NOWAITSKIP LOCKED 仅适用于行级锁 - 仍然以普通方式获取所需的 ROW SHARE 表级锁(请参阅第 13 章)。如果需要在不等待的情况下获取表级锁,则可以先使用LOCK 以及 NOWAIT 选项。

如果在锁定子句中命名了特定的表,则仅锁定来自这些表的行;在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 BYLIMIT 或其他限制结合使用时。因此,仅当预期排序列的并发更新并且需要严格排序的结果时,才建议使用此技术。

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

TABLE 命令

命令

TABLE name

等效于

SELECT * FROM name

它可以用作顶级命令或用作复杂查询部分中的节省空间的语法变体。仅WITHUNIONINTERSECTEXCEPTORDER BYLIMITOFFSETFETCHFOR 锁定子句可与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,后跟查询的递归部分。确保查询的递归部分最终将不返回任何元组,否则查询将无限循环。(有关更多示例,请参阅第 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数据库无法执行此操作,除非引入一个虚拟的一行表从中执行SELECT

SELECT 列表

SELECT 之后的输出表达式列表可以为空,生成一个零列结果表。这根据 SQL 标准不是有效的语法。PostgreSQL 允许它与允许零列表保持一致。但是,当使用DISTINCT 时,不允许使用空列表。

省略AS 关键字

在 SQL 标准中,可以在新列名称为有效列名称(即,与任何保留关键字都不相同)时省略输出列名称之前的可选关键字ASPostgreSQL 稍微更严格一些:如果新列名称与任何关键字(保留或未保留)匹配,则需要AS。建议的做法是使用AS 或双引号输出列名称,以防止与将来添加的关键字发生任何可能的冲突。

FROM 项中,标准和PostgreSQL 都允许在作为未保留关键字的别名之前省略AS。但这对于输出列名称而言是不切实际的,因为存在语法歧义。

FROM 中省略子SELECT 别名

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

ONLY 和继承

SQL 标准要求在编写ONLY 时在表名称周围使用括号,例如SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...PostgreSQL 认为这些括号是可选的。

PostgreSQL 允许编写尾随* 以显式指定包括子表的非ONLY 行为。标准不允许这样做。

(这些要点同样适用于所有支持ONLY 选项的 SQL 命令。)

TABLESAMPLE 子句限制

TABLESAMPLE 子句目前仅在普通表和物化视图上接受。根据 SQL 标准,它应该可以应用于任何 FROM 项目。

FROM 中的函数调用

PostgreSQL 允许将函数调用直接写为 FROM 列表的成员。在 SQL 标准中,需要将此类函数调用包装在子 SELECT 中;也就是说,语法 FROM func(...) alias 约等于 FROM LATERAL (SELECT func(...)) alias。请注意,LATERAL 被认为是隐式的;这是因为标准要求 FROM 中的 UNNEST() 项目具有 LATERAL 语义。PostgreSQLUNNEST() 与其他返回集合的函数视为相同。

GROUP BYORDER BY 可用的命名空间

在 SQL-92 标准中,ORDER BY 子句只能使用输出列名或数字,而 GROUP BY 子句只能使用基于输入列名的表达式。PostgreSQL 扩展了这两个子句,允许使用其他选择(但如果存在歧义,则使用标准的解释)。PostgreSQL 还允许这两个子句指定任意表达式。请注意,表达式中出现的名称将始终被视为输入列名,而不是输出列名。

SQL:1999 及更高版本使用略微不同的定义,该定义与 SQL-92 不完全向上兼容。但是,在大多数情况下,PostgreSQL 将以与 SQL:1999 相同的方式解释 ORDER BYGROUP BY 表达式。

函数依赖性

PostgreSQL 仅当表的 primary key 包含在 GROUP BY 列表中时才识别函数依赖性(允许从 GROUP BY 中省略列)。SQL 标准指定了应该识别的其他条件。

LIMITOFFSET

LIMITOFFSET 子句是 PostgreSQL 特定的语法,MySQL 也使用它。SQL:2008 标准引入了 OFFSET ... FETCH {FIRST|NEXT} ... 子句来实现相同的功能,如上文 LIMIT 子句 中所示。此语法也由 IBM DB2 使用。(为 Oracle 编写的应用程序通常使用涉及自动生成的 rownum 列的变通方法,该列在 PostgreSQL 中不可用,来实现这些子句的效果。)

FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE

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

WITH 中的数据修改语句

PostgreSQL 允许将 INSERTUPDATEDELETEMERGE 用作 WITH 查询。这在 SQL 标准中找不到。

非标准子句

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

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

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

提交更正

如果您在文档中发现任何不正确的内容、与您对特定功能的体验不符的内容或需要进一步澄清的内容,请使用 此表单 报告文档问题。