SELECT

从表或视图中检索行。

概要

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

where <grouping_element> can be one of:

  ()
  <expression>
  ROLLUP (<expression> [,...])
  CUBE (<expression> [,...])
  GROUPING SETS (<grouping_element> [, ...])

where <with_query> is:

  <with_query_name> [( <column_name> [, ...] )] AS ( [ NOT ] MATERIALIZED ] ( <select> | <values> | <insert> | <update> | delete )

TABLE [ ONLY ] <table_name> [ * ]

描述

SELECT 语句从零个或多个表中检索行。SELECT 语句的一般处理过程如下:

  1. WITH子句中的所有查询均被计算。这些查询实质上作为临时表存在,可在FROM列表中被引用。若WITH查询在FROM中被多次引用,则仅计算一次(除非通过NOT MATERIALIZED进行特殊指定)。(详见下文WITH子句说明)
  2. FROM列表中的所有元素均需计算(列表中每个元素均为真实或虚拟表)。若FROM列表包含多个元素,则对它们执行交叉连接(详见下文FROM子句)。
  3. 若指定了WHERE子句,则所有不符合条件的行将从输出中剔除。(详见下文WHERE子句说明。)
  4. 若指定了GROUP BY子句或存在聚合函数调用,则输出将按一个或多个匹配值分组,并计算聚合函数结果。若存在HAVING子句,则会剔除不符合指定条件的组。(详见下文GROUP BY子句和HAVING子句说明。)
  5. 实际输出行是通过为每行或每组选定行计算SELECT输出表达式得出的。(参见下文SELECT列表。)
  6. SELECT DISTINCT可从结果中消除重复行。SELECT DISTINCT ON将移除在所有指定表达式上匹配的行。SELECT ALL(默认选项)将返回所有候选行,包括重复行。(参见下文DISTINCT子句。)
  7. 若指定窗口表达式(及可选的 WINDOW 子句),输出将按位置(行)或值基(范围)窗口帧进行组织。(参见下文 WINDOW 子句)
  8. 使用 UNION、INTERSECT 和 EXCEPT 运算符,可将多个 SELECT 语句的输出组合成单一结果集。UNION 运算符返回存在于其中一个或两个结果集中的所有行。INTERSECT 运算符返回严格存在于两个结果集中的所有行。EXCEPT 运算符返回存在于第一个结果集但不存在于第二个结果集中的行。在所有三种情况下,除非显式指定ALL,否则会消除重复行。可添加关键词DISTINCT来明确要求消除重复行。需注意此处默认行为是DISTINCT,尽管SELECT本身的默认值是ALL。(详见下文UNION子句、INTERSECT子句和EXCEPT子句)
  9. 若指定ORDER BY子句,返回行将按指定顺序排序。未指定时,系统将按生成速度最快的顺序返回行。(详见下文ORDER BY子句说明)
  10. 若指定LIMIT(或FETCH FIRST)或OFFSET子句,SELECT命令仅返回结果行子集。(详见下文LIMIT子句说明)
  11. 若指定了FOR UPDATE、FOR NO KEY UPDATE、FOR SHARE或FOR KEY SHARE,当全局死锁检测器处于停用状态(默认状态)时,SELECT命令将锁定整个表以防止并发更新。当全局死锁检测器处于启用状态时,它会影响某些包含锁定子句的简单SELECT语句。(参见下文的锁定子句。)

在 SELECT 命令中使用的每列都必须具备SELECT权限。若使用 FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 或 FOR KEY SHARE 子句,还需具备 UPDATE 权限(至少对所选每张表中的一列)。

参数

WITH 子句

WITH 子句允许您指定一个或多个子查询,这些子查询可在主查询中通过名称进行引用。在主查询执行期间,这些子查询实质上充当临时表或视图。每个子查询可以是 SELECT、TABLE、VALUES、INSERT、UPDATE 或 DELETE语句。在 WITH 子句中编写数据修改语句(INSERT、UPDATE 或 DELETE)时,通常需包含 RETURNING 子句。主查询读取的临时表由 RETURNING 的输出结果构成,而非语句实际修改的底层表。若省略 RETURNING,语句仍会执行,但不会产生可供主查询作为表引用的输出结果。

对于包含 WITH 子句的 SELECT 命令,该子句最多只能包含一个修改表数据的子句(即 INSERT、UPDATE 或 DELETE 命令)。

每个WITH子查询都必须指定一个名称(不带模式限定)。可选地,你可以指定一组列名;若省略此项,则名称将从子查询中推断出来。

若指定了RECURSIVE,则允许SELECT子查询通过名称引用自身。此类子查询必须采用以下形式:

<non_recursive_term> UNION [ALL | DISTINCT] <recursive_term>

递归自引用出现在UNION的右侧。每个查询仅允许一个递归自引用。不支持递归数据修改语句,但可在数据修改语句中使用递归SELECT查询的结果。

若指定RECURSIVE关键字,WITH子查询无需按顺序排列:当前查询可引用列表中后置的其他查询。但循环引用(即相互递归)不被支持。未指定RECURSIVE关键字时,WITH子查询仅能引用WITH列表中前置的同级WITH子查询。

当WITH子句中包含多个查询时,RECURSIVE应仅在WITH之后立即出现一次。它适用于WITH子句中的所有查询,但对未使用递归或前向引用的查询无效。

WITH RECURSIVE的限制。以下情况不被支持:

  • 一个递归的WITH子句,其<recursive_term>包含以下内容。
    • 具有自我引用的子查询
    • DISTINCT 子句
    • GROUP BY 子句
    • 窗口函数
  • 递归的 WITH 子句,其中 <with_query_name> 是集合运算的一部分。 以下是一个集合运算限制的示例。该查询返回错误,因为集合运算 UNION 包含对表 foo 的引用。
    WITH RECURSIVE foo(i) AS (
      SELECT 1
    UNION ALL
      SELECT i+1 FROM (SELECT * FROM foo UNION SELECT 0) bar
    )
    SELECT * FROM foo LIMIT 5;

    允许使用此递归CTE,因为集合运算UNION未引用CTE foo。

    WITH RECURSIVE foo(i) AS (
      SELECT 1
    UNION ALL
      SELECT i+1 FROM (SELECT * FROM bar UNION SELECT 0) bar, foo
      WHERE foo.i = bar.a
    )
    SELECT * FROM foo LIMIT 5;

    主查询与WITH子查询(理论上)均在同一时间执行。这意味着WITH子查询中数据修改语句的效果无法通过查询的其他部分直接观察到,除非读取其RETURNING输出。若两个此类数据修改语句试图修改同一行数据,则结果行为未定义。

WITH 子查询的一个关键特性在于,它们在主查询每次执行时仅被评估一次,即使主查询多次引用它们也是如此。特别地,数据修改语句被保证仅执行一次且仅此一次,无论主查询是否读取其全部或部分输出结果。

然而,WITH子查询可标记为NOT MATERIALIZED以取消此保证。此时,WITH子查询可被折叠到主查询中,如同在主查询的FROM子句中嵌套一个简单的子SELECT语句。若主查询多次引用该WITH子查询,将导致重复计算;但若每次引用仅需WITH子查询总输出中少量行,则通过允许联合优化查询,NOT MATERIALIZED反而能实现净性能提升。若NOT MATERIALIZED附加于递归WITH查询或非无副作用的WITH查询(例如非不含易失性函数的普通SELECT),则该标记将被忽略。

默认情况下,若WITH子查询在主查询的FROM子句中仅出现一次,则该无副作用的WITH子查询会被折叠到主查询中。这使得在语义上不可见的情况下,能够对两个查询层级进行联合优化。但通过将WITH子查询标记为MATERIALIZED,可阻止此类折叠操作。例如当WITH子查询被用作优化屏障以防止规划器选择不良执行计划时,此特性便具有实用价值。7版之前的数据库从未执行此类折叠操作,因此为旧版本编写的查询可能依赖WITH作为优化屏障。

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子句,表示应使用指定的采样方法从该表中检索部分行。此采样操作优先于任何其他过滤器(如WHERE子句)的应用。标准VMware发行版包含两种采样方法:BERNOULLI和SYSTEM。您可通过扩展程序在数据库中安装额外的采样方法SYSTEM_ROWS和SYSTEM_TIME。

    • BERNOULLI以及SYSTEM采样方法都接受 一个参数,它表示要采样的表 的分数,表示为一个 0 到 100 之间的百分数。这个参数可以是任意的 实数值表达式(其他的采样方法可能接受更多或者不同的 参数)。这两种方法都返回一个随机选取的该表采样,其中包含了指定 百分数的表行。BERNOULLI方法扫描整个表并且 用指定的几率选择或者忽略行。SYSTEM方法会做 块层的采样,每个块都有指定的机会能被选中,被选中块中的所有行都 会被返回。在指定较小的采样百分数时,SYSTEM 方法要比BERNOULLI方法快很多,但是前者可能 由于聚簇效应返回随机性较差的表采样。

SYSTEM ROWS 表采样方法接受单个整数参数,该参数表示最大读取行数。生成的样本始终包含确切数量的行,除非表中行数不足,此时将选取整个表。 SYSTEM_TIME 表采样方法接受单个浮点数参数,该参数指定读取表所需耗费的最大毫秒数。此方法可控制查询耗时,但代价是采样行数难以预测。生成的样本将包含在指定时间内读取到的所有行,除非该表已被提前读取完毕。

与内置的SYSTEM采样方法类似,SYSTEM_ROWS和SYSTEM_TIME执行块级采样,因此样本并非完全随机,可能存在聚类效应,尤其在选取行数较少时更为显著。

在 append-optimized 表上执行采样操作时,若该表存在块目录(即表当前拥有索引或曾拥有索引),采样性能将得到提升。性能提升程度与表的压缩率成正比,与请求采样大小成反比。若需为无索引的表创建块目录,请执行以下命令:

  CREATE INDEX dummy ON tab(i) WHERE false; DROP INDEX dummy;

可选的REPEATABLE子句指定用于在采样方法中生成随机数的种子数或表达式。种子值可以是任何非空浮点数值。若两个查询指定相同的种子值和参数值,且表在此期间未被修改,则将选取表中相同的样本。但不同的种子值通常会产生不同的样本。若未指定REPEATABLE,则数据库会基于系统生成的种子值为每次查询选取新的随机样本。需注意某些附加采样方法不支持REPEATABLE,每次使用时都会产生新的样本。

  • select

    • 子SELECT子句可出现在FROM子句中。其作用如同将输出结果创建为临时表,仅在本SELECT命令执行期间有效。需注意子SELECT子句必须用括号括起,并为其指定别名。此处也可使用VALUES命令。
  • with_query_name

    • 在FROM子句中引用WITH查询时,需通过指定其名称来实现,如同该名称是表名一般。您可采用与表别名相同的方式为其提供别名。

    • WITH子查询会隐藏同名表,以满足主查询的需求。如有必要,可通过添加模式限定符来引用同名表。

  • function_name

    • 函数调用可出现在FROM子句中(对于返回结果集的函数尤为实用,但任何函数均可使用)。这相当于将函数输出视为临时表,仅在本SELECT命令执行期间存在。当函数调用中添加可选的WITH ORDINALITY子句时,数据库会在函数所有输出列之后追加新列,并对每行数据进行编号。
    • 您可以像为表指定别名那样为函数指定别名。若指定了别名,还可指定列别名列表,为函数复合返回类型的一个或多个属性提供替代名称,包括ORDINALITY函数添加的列(若存在)。
    • 通过用ROWS FROM( ... )包裹多个函数调用,可将其合并为单个FROM子句项。此类子句的输出结果为:先将各函数的首行数据拼接,再拼接次行数据,依此类推。若部分函数返回的行数少于其他函数,则用空值替代缺失数据,确保最终返回的总行数始终等于返回行数最多的函数。
    • 若函数被定义为返回记录数据类型,则必须包含别名或AS关键字,后跟列定义列表,格式为(<列名> <数据类型> [, ... ])。列定义列表必须与函数实际返回的列数及类型完全匹配。
    • 使用 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 连接类型,必须指定连接条件,即精确选择 NATURALON <join_condition>USING (<join_column> [, ...]) 其中之一。具体含义详见后续说明。

JOIN子句将两个FROM项组合起来,为方便起见,这些项被称为"表",但实际上它们可以是任何类型的FROM项。必要时使用括号来确定嵌套顺序。若未使用括号,JOIN将按从左到右的顺序嵌套。JOIN的结合力强于分隔FROM列表项的逗号。所有JOIN选项仅为语法简化手段,其功能均可通过普通FROM和WHERE子句实现。

LEFT OUTER JOIN 返回所有符合条件的笛卡尔积行(即所有满足连接条件的组合行),并额外包含左表中每行的一份副本——这些左表行在右表中没有满足连接条件的对应行。这些左表行通过在右表列中插入空值,被扩展至连接表的完整宽度。需注意:判断行匹配时仅考虑 JOIN 子句本身的条件,外部条件将在后续阶段应用。

相反,RIGHT OUTER JOIN 会返回所有连接的行,再加上每条未匹配的右侧行对应的一行(左侧用空值扩展)。这只是表示上的便利,因为通过交换左右表的位置,即可将其转换为 LEFT OUTER JOIN。

全外连接返回所有连接的行,外加每条未匹配的左侧行对应的一行(右侧用空值扩展),以及每条未匹配的右侧行对应的一行(左侧用空值扩展)。

  • ON join_condition
    • join_condition 是一个表达式,其结果为布尔类型(类似于 WHERE 子句),用于指定在连接操作中哪些行被视为匹配。
  • USING (join_column [, ...])
    • USING (a, b, ...) 形式的子句是 ON left_table.a = right_table.a AND left_table.b = right_table.b ... 的简写形式。此外,USING 表示每对等效列中仅包含其中一个列,而非两者同时包含。
  • NATURAL
    • NATURAL 是 USING 子句的简写形式,用于指定两个表中所有同名列。若不存在共同列名,则 NATURAL 等同于 ON TRUE。
  • 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项包含横向交叉引用时,评估过程如下:对于提供交叉引用列的FROM项的每行,或提供列的多行FROM项集合,均使用该行或行集的列值来评估横向项。计算结果行将按常规方式与原始行进行连接。此过程将针对列源表中的每行或行集重复执行。
    • 列源表必须通过内部连接或左连接与LATERAL项关联,否则将无法获得用于计算LATERAL项每组行数据的明确行集。因此,尽管 RIGHT JOIN LATERAL 这类语法结构在语法上有效,但禁止引用

WHERE子句

可选的WHERE子句具有以下一般形式:

WHERE <condition>

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

GROUP BY 子句

可选的 GROUP BY 子句具有以下一般形式:

GROUP BY <grouping_element> [, ...]

其中 <grouping_element> 可以是以下任一元素:

()
<expression>
ROLLUP (<expression> [,...])
CUBE (<expression> [,...])
GROUPING SETS ((<grouping_element> [, ...]))

GROUP BY 将所有具有相同分组表达式值的选定行压缩为单行。分组元素内部使用的表达式可以是输入列名、输出列名(SELECT 列表项)或序号,也可以是由输入列值构成的任意表达式。若存在歧义,则 GROUP BY 名称将被解释为输入列名而非输出列名。

若分组元素中包含任何 GROUPING SETS、ROLLUP 或 CUBE 操作符,则整个 GROUP BY 子句将定义若干独立的分组集。其效果等同于在子查询间构建 UNION ALL 操作,且各子查询的 GROUP BY 子句分别对应独立的分组集。

若使用聚合函数,则会在构成每个分组的所有行上进行计算,为每个分组生成独立的值。(若存在聚合函数但未指定GROUP BY子句,则查询将被视为仅包含所有选定行的单一分组。) 可通过在聚合函数调用中附加 FILTER 子句,进一步筛选传递给各聚合函数的行集。当存在 FILTER 子句时,仅匹配该子句的行会被纳入该聚合函数的输入。详见聚合表达式。

当存在GROUP BY子句或任何聚合函数时,SELECT列表表达式不得引用未分组列,除非该未分组列位于聚合函数内部,或该未分组列在功能上依赖于分组列。否则,未分组列可能返回多个不同值。若分组列(或其子集)构成包含未分组列的表的主键,则存在功能依赖关系。

请注意,所有聚合函数的计算都发生在 HAVING 子句或 SELECT 列表中的任何"标量"表达式计算之前。这意味着,例如,无法使用 CASE 表达式来跳过聚合函数的计算。

目前,FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 和 FOR KEY SHARE 无法与 GROUP BY 一起使用。

数据库具有以下额外的OLAP分组扩展(通常称为超级组):

  • ROLLUP

    • ROLLUP分组是GROUP BY子句的扩展,它根据一组分组列(或表达式)创建从最详细级别逐步汇总至总计的聚合小计。ROLLUP采用有序的分组列列表,计算GROUP BY子句中指定的标准聚合值,然后从列表右向左逐步生成更高层级的小计。最终生成总计值。ROLLUP分组可视为一系列分组集合。例如:
      GROUP BY ROLLUP (a,b,c) 

      等同于:

      GROUP BY GROUPING SETS( (a,b,c), (a,b), (a), () ) 

      请注意,ROLLUP 中的 n 个元素会转换为 n+1 个分组集。此外,在 ROLLUP 中,分组表达式的指定顺序具有重要意义。

  • CUBE

    • CUBE分组是GROUP BY子句的扩展,用于为给定分组列(或表达式)列表的所有可能组合生成小计。在多维分析中,CUBE会为具有指定维度的数据立方体生成所有可计算的小计。例如:
      GROUP BY CUBE (a,b,c) 

      等同于:

      GROUP BY GROUPING SETS( (a,b,c), (a,b), (a,c), (b,c), (a), 
      (b), (c), () ) 

      请注意,CUBE的n个元素对应2n个分组集。在需要交叉表报告的任何场景中,建议使用CUBE。CUBE通常最适用于查询多个维度的列,而非单一维度不同层级的列。例如,常见的交叉表需求可能需要按月、州和产品组合计算所有子总计。

  • GROUPING SETS 您可以在 GROUP BY 子句中使用 GROUPING SETS 表达式,有选择地指定要创建的组集合。这允许在多个维度上进行精确指定,而无需计算完整的 ROLLUP 或 CUBE。例如:

    GROUP BY GROUPING SETS( (a,c), (a,b) )

    若使用分组扩展子句 ROLLUP、CUBE 或 GROUPING SETS,将面临两大挑战。首先,如何确定哪些结果行属于小计,以及如何精确定位特定小计的聚合层级。或者,如何区分结果行中既包含存储的NULL值,又包含由ROLLUP或CUBE生成的"NULL"值的情况?其次,当GROUP BY子句中指定了重复的分组集时,如何确定哪些结果行属于重复项?为此,您可以在SELECT列表中使用两个额外的分组函数:

    • grouping(column [, ...]) — 分组函数可应用于一个或多个分组属性,用于区分超级聚合行与常规分组行。这有助于区分超级聚合行中代表所有值集合的"NULL"与常规行中的NULL值。该函数的每个参数均生成一个位值(1或0),其中1表示结果行属于超聚合,0表示结果行来自常规分组。分组函数通过将这些位值视为二进制数并转换为十进制整数来返回结果。
    • group_id() — 用于处理包含重复分组集的扩展查询,该函数可识别输出结果中的重复行。所有唯一分组集的输出行均具有值为 0 的 <group_id>。对于检测到的每个重复分组集,group_id 函数会分配一个大于 0 的 <group_id> 编号。特定重复分组集中的所有输出行均由相同的 <group_id> 编号标识。

HAVING 子句

可选的HAVING子句具有以下一般形式:

HAVING <condition>

其中 <condition> 与 WHERE 子句中指定的条件相同。

HAVING子句用于过滤不符合条件的分组行。它与WHERE子句不同:WHERE在应用GROUP BY之前过滤单行数据,而HAVING则过滤GROUP BY生成的分组行。在条件表达式中引用的每个列必须明确指向分组列,除非该引用出现在聚合函数内部,或该未分组列在功能上依赖于分组列。

即使没有GROUP BY子句,HAVING的存在也会使查询变成分组查询。这与查询包含聚合函数但没有GROUP BY子句时的情况相同。所有选中的行将被视为构成单一组,且SELECT列表与HAVING子句仅能通过聚合函数内部引用表列。此类查询在HAVING条件为真时返回单行,反之则返回零行。

目前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE 无法与 HAVING 子句同时指定。

WINDOW 子句

可选的 WINDOW 子句用于指定出现在查询 SELECT 列表或 ORDER BY 子句中的窗口函数的行为。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>] 

WINDOW子句条目不必在任何地方被引用;若查询中未使用该条目,则直接忽略。完全可以不使用任何WINDOW子句来调用窗口函数,因为窗口函数调用可在其OVER子句中直接指定窗口定义。但当多个窗口函数需要相同的窗口定义时,WINDOW子句能节省重复编写代码的操作。

例如:

SELECT vendor, rank() OVER (mywindow) FROM sale
GROUP BY vendor
WINDOW mywindow AS (ORDER BY sum(prc*qty));
  • existing_window_name

    • 若指定了 existing_window_name,则该名称必须指向 WINDOW 列表中的先前条目;新窗口将从该条目复制其分区子句,以及(若有的话)排序子句。新窗口不能指定自己的 PARTITION BY 子句,且仅当被复制的窗口未指定排序子句时,才可指定 ORDER BY 子句。新窗口始终使用自己的框架子句;被复制的窗口不得指定框架子句。
  • PARTITION BY

    • PARTITION BY 子句根据指定表达式的唯一值将结果集组织成逻辑分区。该子句的元素解释方式与 GROUP BY 子句基本相同,但它们始终是简单表达式,绝不能是输出列的名称或编号。另一区别在于这些表达式可包含聚合函数调用,而常规 GROUP BY 子句中不允许此类操作。此处允许的原因在于:分区操作发生在分组与聚合之后。当与窗口函数配合使用时,这些函数将独立应用于每个分区。例如,若在 PARTITION BY 后跟随列名,则结果集将按该列的唯一值进行分区;若省略列名,则整个结果集视为单一分区。
  • ORDER BY

    • 同样地,ORDER BY列表中的元素与ORDER BY子句中的元素解释方式基本相同,区别在于这些表达式始终被视为简单表达式,而非输出列的名称或编号。 注意!
      ORDER BY 子句的排序元素定义了如何对结果集每个分区中的行进行排序。若省略该子句,则按最高效的顺序返回行,排序结果可能有所不同。
  • frame_clause

    • 可选的frame_clause用于定义依赖于帧的窗口函数的窗口帧(并非所有窗口函数都依赖帧)。窗口帧是查询中每行数据(称为当前行)的相关行集合。frame_clause可以是以下选项之一:
      { RANGE | ROWS | GROUPS } <frame_start> [ <frame_exclusion> ]
      { RANGE | ROWS | GROUPS } BETWEEN <frame_start> AND <frame_end> [ <frame_exclusion> ]

      其中 <frame_start><frame_end> 可以是以下之一:

      UNBOUNDED PRECEDING
      <offset> PRECEDING
      CURRENT ROW
      <offset> FOLLOWING
      UNBOUNDED FOLLOWING

      以及 <frame_exclusion> 可以是以下之一:

      EXCLUDE CURRENT ROW
      EXCLUDE GROUP
      EXCLUDE TIES
      EXCLUDE NO OTHERS

      若省略<frame_end>,则默认为CURRENT ROW。限制条件为:<frame_start>不可为UNBOUNDED FOLLOWING, <frame_end> 不能是 UNBOUNDED PRECEDING,且 <frame_end> 的选项在上述 <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 则表示帧以分区末行结束——无论采用 RANGEROWS 还是 GROUPS 模式。在ROWS模式下,CURRENT ROW表示帧以当前行开始或结束;但在RANGEGROUPS模式中,它表示帧以当前行在ORDER BY排序中的首个或最后一个同级行开始或结束。<offset> PRECEDING<offset> FOLLOWING 选项的含义随帧模式变化。在 ROWS 模式下,<offset> 是整数,表示帧从当前行之前或之后的指定行数处开始或结束。在GROUPS模式下,<offset>是一个整数,表示帧在当前行同类组之前或之后开始或结束的行数。其中同类组是指根据窗口ORDER BY子句具有等效性的行集合。在RANGE模式下,使用<offset>选项要求窗口定义中必须仅包含一个ORDER BY列。此时帧包含满足以下条件的行:其排序列值比当前行的排序列值小于<offset>(用于PRECEDING)或大于<offset>(用于FOLLOWING)。此时<offset>表达式的数据类型取决于排序列的数据类型。对于数值排序列,其类型通常与排序列相同;但对于日期时间排序列,其类型为间隔类型。在所有这些情况下,<offset> 的值必须为非空且非负数。此外,虽然偏移量不必是简单的常量,但不能包含变量、聚合函数或窗口函数。

    <frame_exclusion> 选项允许将当前行周围的行排除在帧之外,即使根据帧起始和帧结束选项它们本应被包含在内。EXCLUDE CURRENT ROW 将当前行排除在框架之外。EXCLUDE GROUP 将当前行及其排序同级行排除在框架之外。EXCLUDE TIES 将当前行的所有同级行排除在框架之外,但不包括当前行本身。EXCLUDE NO OTHERS 仅明确指定默认行为:不排除当前行及其同级行。

    请注意,当ORDER BY排序未能使行唯一排序时,ROWS模式可能产生不可预测的结果。RANGE和GROUPS模式的设计旨在确保ORDER BY排序中同级行的处理方式一致:特定同级组的所有行要么全部包含在帧中,要么全部排除在帧之外。

    使用 ROWS、RANGE 或 GROUPS 子句来表达窗口的边界。窗口边界可以是分区中的一行、多行或所有行。窗口边界可通过以下方式定义:基于当前行值偏移的数据值范围(RANGE)、基于当前行偏移的行数(ROWS),或基于同级组数(GROUPS)。使用RANGE或GROUPS子句时,必须同时使用ORDER BY子句。这是因为生成窗口所需的计算要求对值进行排序。此外,ORDER BY子句不能包含多个表达式,且该表达式必须返回日期或数值。使用ROWS、RANGE或GROUPS子句时,若仅指定起始行,则当前行将作为窗口的末行。

    PRECEDING — PRECEDING子句以当前行作为基准点定义窗口的首行。起始行通过当前行之前的前行数来表示。例如,在ROWS分框模式下,5 PRECEDING将窗口起始位置设为当前行之前第五行。在范围框选模式下,该设置将窗口起始点设为:在指定排序顺序中,排序列值比当前行早5位的行。若指定排序为日期升序,则对应当前行前5天内的首行。UNBOUNDED PRECEDING将窗口首行设为分区中的首行。

    BETWEEN — BETWEEN 子句通过当前行作为基准点,定义窗口的首尾行。首尾行分别以当前行之前和之后的行数表示。例如,BETWEEN 3 PRECEDING AND 5 FOLLOWING 将窗口设置为从当前行之前第三行开始,至当前行之后第五行结束。使用 BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 可将窗口的首尾行分别设为分区中的首尾行。这相当于未指定 ROWs、RANGE 或 GROUPS 子句时的默认行为。

    FOLLOWING — FOLLOWING子句通过当前行作为基准点来定义窗口的末行。末行通过当前行之后的行数来表示。例如,在ROWS分框模式下,5 FOLLOWING将窗口设置为以当前行之后的第五行作为结束点。在 RANGE 框架中,该设置将窗口终点设为:在指定排序规则下,其排序列值比当前行值晚 5 个的最后一行。若指定排序为日期升序,则对应当前行之后 5 天内的最后一行。使用 UNBOUNDED FOLLOWING 可将窗口末行设为分区中的最后一行。

    若未指定 ROWS、RANGE 或 GROUPS 子句,且使用了 ORDER BY 时,窗口边界将从分区首行开始(UNBOUNDED PRECEDING),并以当前行结束(CURRENT ROW)。若未指定 ORDER BY,则窗口从分区首行开始(UNBOUNDED PRECEDING),并以分区末行结束(UNBOUNDED FOLLOWING)。

    WINDOW子句的目的是指定出现在查询SELECT列表或ORDER BY子句中的窗口函数的行为。这些函数可在其OVER子句中通过名称引用WINDOW子句条目。但窗口子句条目并非必须被引用;若查询中未使用该条目,则直接忽略。完全不使用窗口子句也能调用窗口函数,因为窗口函数调用可在其OVER子句中直接指定窗口定义。不过当多个窗口函数需要相同窗口定义时,使用窗口子句可节省重复编写代码的操作。

    目前,FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 和 FOR KEY SHARE 无法与 WINDOW 同时指定。 有关窗口函数的更多信息,请参阅窗口表达式。

SELECT 列表

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

SELECT 列表中的表达式可以是常量值、列引用、运算符调用、函数调用、聚合表达式、窗口表达式、标量子查询等。某些构造虽可归类为表达式,但不遵循任何通用语法规则,通常具有函数或运算符的语义。

正如在表中一样,SELECT语句的每个输出列都有一个名称。在简单的SELECT语句中,该名称仅用于标记列以供显示;但当SELECT作为更大查询的子查询时,该名称会被更大查询视为子查询生成的虚拟表的列名。要指定输出列的名称,请在列表达式后添加AS <输出名称>。(可省略AS,但仅限于目标输出名称不与任何SQL关键字冲突的情况。为防范未来可能新增的关键字,建议始终使用AS或为输出名称添加双引号。)若未指定列名,数据库将自动生成名称。当列表达式为简单列引用时,生成的名称与该列原名相同。在更复杂的情况下,系统可能使用函数名或类型名,或回退至生成名称如?column?columnN

输出列的名称可在ORDER BY和GROUP BY子句中引用该列的值,但不能在WHERE或HAVING子句中使用;后者必须指定表达式。

在输出列表中,可使用 作为所选行所有列的简写替代表达式。此外,`<表名>.` 可作为仅来自该表的列的简写。此类情况下无法通过 AS 指定新名称,输出列名将与表列名保持一致。

根据SQL标准,输出列表中的表达式应在应用DISTINCTORDER BYLIMIT之前计算。使用DISTINCT时这显然是必要的,否则无法确定哪些值需要去重。但在许多情况下,若将输出表达式置于ORDER BYLIMIT之后计算会更便捷——尤其当输出列表包含易变或耗时的函数时。这种行为使得函数评估的顺序更符合直觉,且不会出现对应于从未出现在输出中的行进行评估的情况。只要输出表达式未在DISTINCTORDER BYGROUP BY中被引用,数据库就会在排序和限制之后有效地评估这些表达式。(反例说明:SELECT f(x) FROM tab ORDER BY 1 显然必须在排序前评估 f(x)。)包含集合返回函数的输出表达式,实际会在排序后、限制前进行评估,因此 LIMIT 将对集合返回函数的输出进行截断。

DISTINCT 子句

若指定 SELECT DISTINCT,则结果集中所有重复行将被移除(每组重复行仅保留一行)。SELECT ALL 则指定相反操作:保留所有行;此为默认行为。

SELECT DISTINCT ON ( <expression> [, ...] ) 仅保留每个表达式结果相等的行组中的首行。DISTINCT ON 表达式的解释规则与 ORDER BY 相同(参见上文)。请注意,除非使用 ORDER BY 确保目标行排在首位,否则每个行组的"首行"是不可预测的。例如:

SELECT DISTINCT ON (location) location, time, report
    FROM weather_reports
    ORDER BY location, time DESC;

检索每个地点的最新天气报告。但如果我们没有使用ORDER BY强制按时间值降序排列每个地点的数据,那么每个地点的报告都将来自不可预测的时间点。

DISTINCT ON 表达式必须与最左侧的 ORDER BY 表达式匹配。ORDER BY 子句通常包含额外的表达式,用于确定每个 DISTINCT ON 分组内行数据的优先级顺序。

目前,FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE 和 FOR KEY SHARE 无法与 DISTINCT 同时指定。

UNION 子句

UNION 子句具有以下一般形式:

<select_statement> UNION [ALL | DISTINCT] <select_statement>

<select_statement> 是任何不包含 ORDER 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运算符按从左到右顺序求值,除非括号标明了其他顺序。

目前,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 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)

目前,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 SHAREFOR KEY SHARE 子句的 SELECT 语句。

EXCEPT 运算符计算左侧 SELECT 语句结果中包含、但右侧结果中不包含的行集合。

除非指定 ALL 选项,否则 EXCEPT 的结果集不包含任何重复行。启用 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;

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

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

可选地在ORDER BY子句的任何表达式后添加ASC(升序)或DESC(降序)关键字。若未指定,则默认采用ASC。或者,可在USING子句中指定特定排序运算符名称。通常 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 的含义并不相同。

字符串数据根据数据库创建时设定的区域设置特定排序规则进行排序。必要时可通过在表达式中添加COLLATE子句覆盖此设置,例如ORDER BY mycolumn COLLATE "en_US"

字符串数据将根据排序列适用的排序规则进行排序。可通过在表达式中添加COLLATE子句覆盖默认排序规则,例如ORDER BY mycolumn COLLATE "en_US"。有关定义排序规则的详细信息,请参阅CREATE COLLATION

LIMIT 子句

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

LIMIT {<count> | ALL}
OFFSET <start>

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

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

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

OFFSET <start> [ ROW | ROWS ]
    FETCH { FIRST | NEXT } [ <count> ] { ROW | ROWS } ONLY

在此语法中,标准要求<start><count>值必须是字面常量、参数或变量名;作为数据库扩展,允许使用其他表达式,但通常需要用括号括起来以避免歧义。若在FETCH子句中省略<count>,则默认为1。ROWROWS以及FIRSTNEXT均为无实际影响的噪声词。根据标准规定,当同时存在OFFSET子句和FETCH子句时,前者必须位于后者之前;但数据库允许任意顺序。

使用LIMIT时,建议配合ORDER BY子句将结果行限定为唯一排序。否则将获得查询行中不可预测的子集——你可能要求获取第10行至第20行,但究竟是在什么排序下的第10行至第20行?除非指定ORDER BY,否则无法确定排序方式。

查询优化器在生成查询计划时会考虑LIMIT参数,因此根据您使用的LIMITOFFSET值不同,很可能会得到不同的计划(导致行序不同)。因此,若使用不同LIMIT/OFFSET值来选择查询结果的不同子集,将导致结果不一致——除非通过ORDER BY强制实现可预测的结果排序。这并非缺陷,而是SQL语言的固有特性:除非使用ORDER BY约束排序,否则SQL不保证以特定顺序返回查询结果。

即使多次执行相同的LIMIT查询,也可能返回表中不同子集的行,除非通过ORDER BY强制选择确定性子集。同样,这并非错误;在此情况下无法保证结果的确定性。

锁定子句

FOR UPDATEFOR NO KEY UPDATEFOR SHAREFOR KEY SHARE 是锁定子句;它们影响 SELECT 语句从表中获取行时的锁定方式。全局死锁检测器会影响包含锁定子句(FOR <lock_strength>)的 SELECT 查询所使用的锁定机制。通过将 gp_enable_global_deadlock_detector 配置参数设置为 on 可启用全局死锁检测器。

锁定子句的一般形式为:

FOR <lock_strength> [OF <table_name> [ , ... ] ] [ NOWAIT | SKIP LOCKED ] 

<lock_strength> 可取以下值之一:

  • UPDATE - 使用排他锁锁定表。
  • NO KEY UPDATE - 使用排他锁锁定表。
  • SHARE - 使用行共享锁锁定表。
  • KEY SHARE - 使用行共享锁锁定表。 当全局死锁检测器处于禁用状态(默认)时,数据库将使用指定的锁。

当全局死锁检测器启用时,对于包含锁定子句且查询计划中包含lockrows节点的简单SELECT查询,将使用ROW SHARE锁来锁定表。包含锁定子句的简单SELECT查询需满足以下所有条件:

  • 锁定子句位于顶级SELECT上下文中。
  • FROM子句仅包含单个表,且该表非视图或 append 优化表。
  • SELECT命令不包含UNIONINTERSECT等集合运算。
  • SELECT命令不包含子查询。 否则,包含锁定子句的 SELECT 查询的表锁定行为将视为全局死锁检测器已禁用。

注意! 全局死锁检测器也会影响DELETE和UPDATE操作使用的锁定机制。默认情况下,数据库对堆表的DELETE和UPDATE操作会获取表的排他锁。当全局死锁检测器启用时,堆表上DELETE和UPDATE操作的锁定模式将变为行排他锁。

有关每种行级锁定模式的更多信息,请参阅 PostgreSQL 文档中的显式锁定部分。

为避免操作等待其他事务提交,可使用NOWAIT选项或SKIP LOCKED选项。启用NOWAIT时,若无法立即锁定选定行,语句将报告错误而非等待;启用SKIP LOCKED时,任何无法立即锁定的选定行将被跳过。跳过锁定行会导致数据视图不一致,因此不适用于常规操作,但可用于避免多个消费者访问队列式表时引发的锁竞争。需注意:NOWAIT与SKIP LOCKED仅适用于行级锁——必需的ROW SHARE表级锁仍按常规方式获取。若需无等待获取表级锁,可先使用带NOWAIT选项的LOCK语句。

如果锁定子句中指定了特定表名,则仅锁定来自这些表的行;SELECT语句中使用的其他表仍按常规方式读取。未包含表列表的锁定子句将影响语句中使用的所有表。若锁定子句应用于视图或子查询,则影响该视图或子查询中使用的所有表。但这些子句不适用于主查询引用的WITH子查询。若需在WITH子查询内实现行级锁定,请在该子查询内部显式指定锁定子句。

若需为不同表指定不同的锁定行为,可编写多个锁定子句。当同一张表被多个锁定子句提及(或隐式影响)时,其处理方式等同于仅由最强的子句指定。同样地,若影响该表的任一子句指定了NOWAIT,则该表将按NOWAIT方式处理。否则,若存在任何影响该表的子句指定SKIP LOCKED,则按此方式处理。

如果被返回的行无法清晰地与表中的行保持一致,则不能使用锁定子句。 例如锁定子句不能与聚集一起使用。

当一个锁定子句出现在一个SELECT查询的顶层时, 被锁定的行正好就是该查询返回的行。在连接查询的情况下,被锁定 的行是那些对返回的连接行有贡献的行。此外,自该查询的快照起满足 查询条件的行将被锁定,如果它们在该快照后被更新并且不再满足 查询条件,它们将不会被返回。如果使用了LIMIT,只要 已经返回的行数满足了限制,锁定就会停止(但注意被 OFFSET跳过的行将被锁定)。类似地,如果在一个游标 的查询中使用锁定子句,只有被该游标实际取出或者跳过的行才将被 锁定。

当锁定子句出现在子SELECT语句中时,被锁定的行是子查询返回给外部查询的行。由于外部查询的条件可能被用于优化子查询的执行,实际锁定的行数可能少于仅检查子查询本身所暗示的数量。例如:

SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;

仅锁定满足col1 = 5条件的行,即使该条件在子查询中并未以文本形式出现。

在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);

要对所有电影的len列求和并且用 kind对结果分组:

SELECT kind, sum(length) AS total FROM films GROUP BY kind;

要对所有电影的len列求和、对结果按照 kind分组并且显示总长小于 5 小时的分组:

SELECT kind, sum(length) AS total FROM films GROUP BY kind 
  HAVING sum(length) < interval '5 hours';

计算按电影类型和发行商划分的销售额小计和总计。

SELECT kind, distributor, sum(prc*qty) FROM sales
GROUP BY ROLLUP(kind, distributor)
ORDER BY 1,2,3;

根据总销售额计算电影发行商的排名:

SELECT distributor, sum(prc*qty), 
       rank() OVER (ORDER BY sum(prc*qty) DESC) 
FROM sales
GROUP BY distributor ORDER BY 2 DESC;

以下两个示例是根据第二列(名称)的内容对单个结果进行排序的相同方法:

SELECT * FROM distributors ORDER BY name;
SELECT * FROM distributors ORDER BY 2;

下例演示了如何获取表 distributors 和 actors 的并集,同时限制结果为每个表中以字母 W 开头的记录。由于仅需唯一行,故省略了 ALL 关键字:

SELECT distributors.name
  FROM distributors
  WHERE distributors.name LIKE 'W%'
UNION
SELECT actors.name
  FROM actors
  WHERE actors.name LIKE 'W%';

此示例展示了如何在FROM子句中使用函数,包括带列定义列表和不带列定义列表的两种情况:

CREATE FUNCTION distributors(int) RETURNS SETOF distributors 
AS $$ SELECT * FROM distributors WHERE did = $1; $$ LANGUAGE 
SQL;
SELECT * FROM distributors(111);

CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS 
$$ SELECT * FROM distributors WHERE did = $1; $$ LANGUAGE 
SQL;
SELECT * FROM distributors_2(111) AS (dist_id int, dist_name text);

此示例使用添加了序列号列的函数:

SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY;
This example uses a simple WITH clause:

WITH test AS (
  SELECT random() as x FROM generate_series(1, 3)
  )
SELECT * FROM test
UNION ALL
SELECT * FROM test; 

此示例使用WITH子句,仅在销售额最高的地区显示每种产品的销售总额。

WITH regional_sales AS 
    SELECT region, SUM(amount) AS total_sales
    FROM orders
    GROUP BY region
  ), top_regions AS (
    SELECT region
    FROM regional_sales
    WHERE total_sales > (SELECT SUM(total_sales) FROM
       regional_sales)
  )
SELECT region, product, SUM(quantity) AS product_units,
   SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions) 
GROUP BY region, product;

该示例本可省略WITH子句,但需使用两层嵌套的子SELECT语句。

此示例使用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 [ALL] 操作符,最后是查询的递归部分。请确保递归部分最终返回的元组数为零,否则查询将陷入无限循环。

这个例子使用LATERAL为manufacturers 表的每一行应用一个集合返回函数get_product_names():

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

当前没有产品的制造商不会出现在结果中,因为这是个内连接。若希望将此类制造商的名称纳入结果,可将查询改写如下:

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

兼容性

SELECT 语句符合 SQL 标准,但存在某些扩展功能和缺失特性。

省略的FROM子句

数据库允许省略FROM子句。它可直接用于计算简单表达式的结果。例如:

SELECT 2+2;

其他某些SQL数据库无法实现此功能,除非引入一个虚假的单行表来执行SELECT操作。

请注意,如果未指定 FROM 子句,则查询无法引用任何数据库表。例如,以下查询无效:

SELECT distributors.* WHERE distributors.name = 'Westward';

在早期版本中,将服务器配置参数 add_missing_from 设置为 true 可使数据库为查询引用的每张表在查询的 FROM 子句中添加隐式条目。此功能现已禁用。

空的SELECT列表

SELECT 语句后的输出表达式列表可以为空,从而生成零列结果表。根据 SQL 标准,这并非有效的语法。数据库允许此操作以保持与允许零列表的一致性。但当使用 DISTINCT 时,不允许使用空列表。

省略AS关键字

在SQL标准中,当新列名是有效的列名(即不与任何保留关键字相同)时,输出列名前可省略可选关键字AS。数据库规范稍严格:若新列名与任何关键字(无论是否为保留字)匹配,则必须使用AS。推荐做法是使用AS或双引号包裹输出列名,以避免未来新增关键字时可能引发的冲突。

在FROM子句中,标准规范与数据库规范均允许省略AS修饰符,前提是别名本身为非保留关键字。但此做法对输出列名并不实用,因其会引发语法歧义。

ONLY 和继承

SQL标准要求在编写ONLY时需在表名两侧添加括号,例如:

SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...

数据库认为这些括号是可选的。

数据库允许在末尾添加*来显式指定包含子表的非ONLY行为。标准不允许这样做。

注:上述要点同样适用于所有支持ONLY选项的SQL命令。

FROM子句中的函数调用

数据库允许您将函数调用直接作为FROM列表的成员编写。在SQL标准中,此类函数调用必须包裹在子SELECT语句中;也就是说,语法FROM func(...)别名大致等同于FROM LATERAL (SELECT func(...))别名。需注意LATERAL被视为隐式存在;这是因为标准要求FROM中的UNNEST()项必须遵循LATERAL语义。数据库将UNNEST()与其他返回集合的函数同等对待。

可用于 GROUP BY 和 ORDER BY 的命名空间

在SQL-92标准中,ORDER BY子句只能使用输出列名或序号,而GROUP BY子句只能使用基于输入列名的表达式。数据库扩展了这两个子句,允许使用另一种选择(但若存在歧义则采用标准解释)。数据库还允许两个子句均指定任意表达式。需注意:表达式中出现的名称始终被视为输入列名,而非输出列名。

SQL:1999 及后续版本采用略有不同的定义,与 SQL-92 并非完全向上兼容。但在多数情况下,数据库对 ORDER BY 或 GROUP BY 表达式的解释方式与 SQL:1999 保持一致。

函数依赖

数据库仅在表的主键包含在GROUP BY列表中时,才识别功能依赖关系(允许省略GROUP BY中的列)。SQL标准规定了应识别的其他条件。

LIMIT 和 OFFSET

LIMITOFFSET 子句是数据库特有的语法,MySQL 也使用这些子句。SQL:2008 标准引入了 OFFSET .. FETCH {FIRST|NEXT} ... 子句来实现相同功能,如上文 LIMIT 子句所示。IBM DB2 也使用此语法。(Oracle应用程序常采用一种变通方案,利用自动生成的rownum列实现这些子句的效果,但该列在Database中不可用。)

FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE 和 FOR KEY SHARE

尽管FOR UPDATE出现在SQL标准中,但该标准仅允许其作为DECLARE CURSOR的选项。数据库允许在任何SELECT查询以及子SELECT中使用它,但这是扩展功能。FOR NO KEY UPDATE、FOR SHARE和FOR KEY SHARE变体,以及NOWAIT和SKIP LOCKED选项均未出现在标准中。

WITH子句中的数据修改语句

YMatrix 允许将INSERT、UPDATE和DELETE用作WITH子查询。此特性未在SQL标准中定义。

非标准条款

DISTINCT ON 子句未在 SQL 标准中定义。

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

WITH 中的 MATERIALIZEDNOT MATERIALIZED 选项是 SQL 标准的扩展。

STABLE 和 VOLATILE 函数的有限用途

为防止数据库中各分区间的数据出现不同步,任何被归类为STABLE或VOLATILE的函数,若包含SQL语句或以任何方式修改数据库,均不得在分区数据库层级运行。更多信息请参阅CREATE FUNCTION

另见

EXPLAIN