2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 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 列表中引用。如果一个 WITH 查询在 FROM 中被引用多次,则只计算一次,除非使用 NOT MATERIALIZED 明确指定。 (参见下面的 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 可以被添加以明确指定删除重复行。请注意,DISTINCT 在这里是默认行为,尽管 ALLSELECT 本身的默认行为。 (参见下面的 UNION 子句, INTERSECT 子句, 和 EXCEPT 子句)。

  8. 如果指定了 ORDER BY 子句,则返回的行将按指定的顺序排序。如果未给出 ORDER BY,则系统会以最快的速度返回行。 (参见下面的 ORDER BY 子句)。

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

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

您必须对 SELECT 命令中使用的每个列拥有 SELECT 权限。使用 FOR NO KEY UPDATE, FOR UPDATE, FOR SHAREFOR KEY SHARE 还需要 UPDATE 权限 (至少对每个所选表的其中一列)。

参数

WITH 子句

WITH 子句允许您指定一个或多个子查询,这些子查询可以被主查询引用。这些子查询在主查询的持续时间内有效地充当临时表或视图。每个子查询都可以是 SELECT, TABLE, VALUES, INSERT, UPDATE, DELETE, 或 MERGE 语句。在 WITH 中编写数据修改语句 (INSERT, UPDATE, DELETE, 或 MERGE) 时,通常会包含一个 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 可以通过允许查询进行联合优化来提供净节省。如果 NOT MATERIALIZED 附加到一个递归的 WITH 查询或不是无副作用的 (SELECT 包含无副作用函数) 查询,则会忽略它。

默认情况下,无副作用的 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

FROM 子句中可以出现子 SELECT。这就像在本次 SELECT 命令的持续时间内,其输出被创建为一个临时表。请注意,子 SELECT 必须用括号括起来,并且可以以与表相同的方式提供别名。这里也可以使用 VALUES 命令。

with_query_name

WITH 查询通过编写其名称来引用,就像该查询的名称是一个表名一样。(实际上,WITH 查询在主查询的范围内会隐藏同名的真实表。如有必要,您可以通过模式限定表名来引用同名的真实表。) 可以以与表相同的方式提供别名。

function_name

函数调用可以出现在 FROM 子句中。(这对于返回结果集的函数特别有用,但任何函数都可以使用。) 这就像在本次 SELECT 命令的持续时间内,函数输出被创建为一个临时表。如果函数的返回类型是复合类型 (包括具有多个 OUT 参数的函数),则每个属性都成为隐式表中的一个独立列。

当可选的 WITH ORDINALITY 子句添加到函数调用时,一个类型为 bigint 的附加列将被附加到函数的列结果中。此列对函数结果集的行进行编号,从 1 开始。默认情况下,此列命名为 ordinality

可以以与表相同的方式提供别名。如果写了别名,也可以写一个列别名列表,为函数复合返回类型的一个或多个属性提供替代名称,包括存在时的序数序数字段。

多个函数调用可以通过用 ROWS FROM( ... ) 包围来组合成一个 FROM 子句项。此类项的输出是将每个函数的第一个行连接起来,然后是每个函数的第二个行,依此类推。如果某些函数产生的行数少于其他函数,则用 null 值替换缺失的数据,因此返回的总行数始终与生成行数最多的函数相同。

如果函数被定义为返回 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_condition, USING (join_column [, ...]), 或 NATURAL 中的一个。有关含义,请参见下文。

JOIN 子句组合了两个 FROM 项,为了方便起见,我们将它们称为“表”,尽管实际上它们可以是任何类型的 FROM 项。如有必要,请使用括号确定嵌套顺序。在没有括号的情况下,JOINs 从左到右嵌套。无论如何,JOIN 的结合性比分隔 FROM 列表项的逗号更强。所有 JOIN 选项都只是一个方便的符号,因为它们不会做任何无法通过普通 FROMWHERE 完成的事情。

LEFT OUTER JOIN 返回合格笛卡尔积中的所有行 (即,通过其连接条件的组合行),再加上左表中没有右表行满足连接条件的每个行的副本。此左表行通过插入右表列的 null 值来扩展到连接表的全部宽度。请注意,在决定哪些行匹配时,只考虑 JOIN 子句本身的条件。外部条件在之后应用。

相反,RIGHT OUTER JOIN 返回所有连接的行,再加上每个未匹配的右表行的行 (在左侧用 null 值扩展)。这只是一个方便的符号,因为您可以通过交换左右表将其转换为 LEFT OUTER JOIN

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

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 项会使用该行或行集的值的列来计算。结果行将像往常一样与它们被计算出的行进行连接。这会为列源表中的每一行或行集重复进行。

列源表必须是 INNERLEFT 连接到 LATERAL 项,否则将不存在一个明确定义的行集来计算 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 SETS, ROLLUPCUBE 中的任何一个作为分组元素存在,那么整个 GROUP BY 子句就定义了若干独立的 grouping sets。其效果等同于在具有单个分组集的子查询之间构建 UNION ALL。可选的 DISTINCT 子句在处理前删除重复集;它 不会UNION ALL 转换为 UNION DISTINCT。有关分组集的处理方式,请参见 第 7.2.4 节

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

当存在 GROUP BY 或任何聚合函数时,SELECT 列表表达式引用未分组的列 (除非在聚合函数内或未分组列在函数上依赖于分组列) 是无效的,因为否则未分组列将有多个可能返回值。如果分组列 (或其子集) 是包含未分组列的表的主键,则存在函数依赖关系。

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

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

HAVING 子句

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

HAVING condition

其中 conditionWHERE 子句指定的相同。

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

HAVING 的存在会将一个查询变成一个分组查询,即使没有 GROUP BY 子句。这与查询包含聚合函数但没有 GROUP BY 子时发生的情况相同。所有选定的行被视为形成一个单一的组,并且 SELECT 列表和 HAVING 子句只能从聚合函数中引用表列。这样的查询将在 HAVING 条件为真时发出单行,如果条件为假则发出零行。

目前,FOR NO KEY UPDATE, FOR UPDATE, FOR 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。新窗口始终使用自己的框架子句;复制的窗口不得指定框架子句。

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 意味着框架以分区的最后一个行结束,无论 RANGE, ROWSGROUPS 模式如何。在 ROWS 模式下,CURRENT ROW 意味着框架以当前行开始或结束;但在 RANGEGROUPS 模式下,它意味着框架以当前行的第一个或最后一个对等行开始或结束。 offset PRECEDINGoffset FOLLOWING 选项的含义取决于框架模式。在 ROWS 模式下,offset 是一个整数,表示框架相对于当前行开始或结束 offset 行。在 GROUPS 模式下,offset 是一个整数,表示框架相对于当前行的对等组开始或结束 offset 个对等组,其中 对等组 是根据窗口的 ORDER BY 子句等效的行组。在 RANGE 模式下,使用 offset 选项要求窗口定义中只有一列 ORDER BY。然后,框架包含那些排序列值不大于 (对于 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 UPDATE, FOR UPDATE, FOR SHAREFOR KEY SHARE 不能与 WINDOW 一起指定。

窗口函数在 第 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 标准,输出列表中的表达式应在应用 DISTINCT, ORDER BY, 或 LIMIT 之前进行计算。这在使用 DISTINCT 时显然是必要的,因为否则就不清楚哪些值正在被区分。然而,在许多情况下,如果输出表达式在 ORDER BYLIMIT 之后进行计算会很方便;特别是当输出列表包含任何易变或昂贵的函数时。有了这种行为,函数求值的顺序就更直观了,也不会有与永远不会出现在输出中的行相对应的求值。 PostgreSQL 将有效地在排序和限制之后计算输出表达式,只要这些表达式未在 DISTINCT, ORDER BYGROUP BY 中引用。(作为反例,SELECT f(x) FROM tab ORDER BY 1 显然必须在排序之前计算 f(x)。) 包含集合返回函数的输出表达式有效地在排序后和限制前计算,因此 LIMIT 将作用于截断集合返回函数的输出。

注意

PostgreSQL 9.6 之前的版本未对输出表达式与排序和限制的求值时序提供任何保证;这取决于所选查询计划的形式。

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 SHAREFOR 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 BYLIMIT。没有括号时,这些子句将被视为应用于 UNION 的结果,而不是其右侧输入表达式。)

UNION 操作符计算参与的 SELECT 语句返回的行的集合并集。一个行存在于两个结果集的集合并集中,如果它存在于至少一个结果集中。代表 UNION 直接操作数的两个 SELECT 语句必须产生相同数量的列,并且对应的列必须是兼容的数据类型。

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

同一个 SELECT 语句中的多个 UNION 操作符按从左到右的顺序进行计算,除非另有指示 (例如通过括号)。

目前,FOR NO KEY UPDATE, FOR UPDATE, FOR SHAREFOR 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 语句返回的行的集合交集。一个行存在于两个结果集的交集中,如果它存在于两个结果集中。

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

同一个 SELECT 语句中的多个 INTERSECT 操作符按从左到右的顺序进行计算,除非括号另有规定。 INTERSECT 的结合性比 UNION 更强。也就是说,A UNION B INTERSECT C 将被读作 A UNION (B INTERSECT C)

目前,FOR NO KEY UPDATE, FOR UPDATE, FOR SHAREFOR 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 语句的结果中存在但不在右侧结果中存在的行集。

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

同一个 SELECT 语句中的多个 EXCEPT 操作符按从左到右的顺序进行计算,除非括号另有规定。 EXCEPT 的结合性与 UNION 相同。

目前,FOR NO KEY UPDATE, FOR UPDATE, FOR 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;

此功能的一个限制是,应用于 UNION, INTERSECT, 或 EXCEPT 子句结果的 ORDER BY 子句只能指定输出列名称或编号,而不能指定表达式。

如果 ORDER BY 表达式是一个简单的名称,它同时匹配输出列名称和输入列名称,ORDER BY 会将其解释为输出列名称。这与 GROUP BY 在同一情况下会做的选择相反。这种不一致是为了与 SQL 标准兼容。

可选地,您可以在任何 ORDER BY 子句的表达式后添加关键字 ASC (升序) 或 DESC (降序)。如果未指定,则默认假定为 ASC。或者,可以在 USING 子句中指定一个特定的排序运算符名称。排序运算符必须是某个 B-tree 运算符族的“小于”或“大于”成员。 ASC 通常等同于 USING <,而 DESC 通常等同于 USING >。(但用户定义数据类型的创建者可以确切地定义默认排序顺序,并且它可能对应于其他名称的运算符。)

如果指定了 NULLS LAST,则 null 值排序在所有非 null 值之后;如果指定了 NULLS FIRST,则 null 值排序在所有非 null 值之前。如果未指定,则当指定或隐含 ASC 时,默认行为是 NULLS LAST,当指定 DESC 时,默认行为是 NULLS FIRST (因此,默认行为就像 null 值比非 null 值大)。使用 USING 时,默认的 null 排序取决于运算符是“小于”还是“大于”运算符。

请注意,排序选项仅适用于它们后面的表达式;例如 ORDER BY x, y DESC 并不意味着 ORDER 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 是噪声词,不会影响这些子句的效果。根据标准,如果 OFFSETFETCH 子句都存在,则 OFFSET 子句必须出现在 FETCH 子句之前;但 PostgreSQL 比较宽松,允许任何顺序。

使用 LIMIT 时,最好使用 ORDER BY 子句来约束结果行以获得唯一顺序。否则,您将获得查询行的一个不可预测的子集——您可能正在请求第 10 到第 20 行,但按什么顺序?除非您指定 ORDER BY,否则您不知道是什么顺序。

查询规划器在生成查询计划时会考虑 LIMIT,因此您很可能会根据 LIMITOFFSET 的使用情况获得不同的计划 (产生不同的行顺序)。因此,使用不同的 LIMIT/OFFSET 值选择查询结果的不同子集 将导致不一致的结果,除非您使用 ORDER BY 来强制执行可预测的结果顺序。这不是一个错误;这是 SQL 不保证按任何特定顺序返回查询结果 (除非使用 ORDER BY 来约束顺序) 的固有后果。

如果没有 ORDER BY 来强制选择确定性子集,重复执行相同的 LIMIT 查询甚至可能返回表行的不同子集。同样,这不是一个错误;在这种情况下,结果的确定性只是不被保证。

锁定子句

FOR UPDATE, FOR NO KEY UPDATE, FOR 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。有关每种行级锁模式的更多信息,请参阅 第 13.3.2 节

为防止操作等待其他事务提交,请使用 NOWAITSKIP LOCKED 选项。使用 NOWAIT 时,如果选定的行无法立即锁定,则该语句会报告错误,而不是等待。使用 SKIP LOCKED 时,任何无法立即锁定的选定行都会被跳过。跳过锁定的行会提供不一致的数据视图,因此不适用于通用工作,但可用于避免多个使用者访问类似队列的表时发生锁争用。请注意,NOWAITSKIP LOCKED 仅适用于行级锁 — 所需的 ROW SHARE 表级锁仍以常规方式获取(参见 第 13 章)。如果需要获取表级锁而不等待,可以首先使用带 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 事务隔离级别下运行的 SELECT 命令,如果使用了 ORDER BY 和锁定子句,可能会返回乱序的行。这是因为 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 标准中,当新的列名是有效列名(即不与任何保留关键字相同)时,输出列名之前的可选关键字 AS 可以被省略。PostgreSQL 稍微严格一些:如果新的列名与任何关键字(无论是否保留)匹配,则需要 AS。建议的做法是使用 AS 或双引号括起输出列名,以防止与未来关键字添加发生任何可能的冲突。

FROM 项中,标准和 PostgreSQL 都允许在未保留关键字的别名前省略 AS。但由于语法上的歧义,这对于输出列名来说是不切实际的。

FROM 中省略子 SELECT 别名

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

ONLY 和继承

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

PostgreSQL 允许在末尾添加 * 来显式指定非 ONLY 行为,即包含子表。PostgreSQL 不允许这样做。

(这些要点同样适用于所有支持 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.html) 包含在 GROUP BY 列表中时,才识别函数依赖(允许从 GROUP BY 中省略列)。SQL 标准规定了应识别的附加条件。

LIMITOFFSET

LIMITOFFSET 子句是 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 查询以及子 SELECTs 中,但这是一种扩展。FOR NO KEY UPDATEFOR SHAREFOR KEY SHARE 变体,以及 NOWAITSKIP LOCKED 选项,均未出现在标准中。

WITH 中的数据修改语句

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

非标准子句

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

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

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

提交更正

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