SELECT
SELECT¶
从表或视图中检索行。
- 语法
-
``` sql [ WITH [ RECURSIVE1 ] 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] […]]
TABLE { [ ONLY ] table_name [ * ] | with_query_name }
其中`with_query:`是: ``` sql with_query_name [( column_name [, ...] )] AS ( select | values | insert | update | delete )
其中
from_item
可以是以下之一:[ONLY] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] ( select ) [ AS ] alias [( column_alias [, ...] ) ] with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ] function_name ( [ argument [, ...] ] ) [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] function_name ( [ argument [, ...] ] ) [ AS ] alias ( column_definition [, ...] ) function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] ) ROWS FROM( function_name ( [ argument [, ...] ] ) [ AS ( column_definition [, ...] ) ] [, ...] ) [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]
其中
grouping_element
可以是以下之一:() expression ROLLUP (expression [,...]) CUBE (expression [,...]) GROUPING SETS ((grouping_element [, ...]))
其中
window_definition
是:[existing_window_name] [PARTITION BY expression [, ...]] [ORDER BY expression [ASC | DESC | USING operator] [NULLS {FIRST | LAST}] [, ...]] [{ RANGE | ROWS} frame_start | {RANGE | ROWS} BETWEEN frame_start AND frame_end]
其中
frame_start
和frame_end
可以是以下之一:UNBOUNDED PRECEDING value PRECEDING CURRENT ROW value FOLLOWING UNBOUNDED FOLLOWING
- 描述
-
SELECT
从零个或多个表中检索行。SELECT
的一般处理如下:-
WITH
子句中的所有查询均被计算。 这些可以有效地用作可以在FROM
列表中引用的临时表。 -
将计算
FROM
列表中的所有元素。(FROM
列表中的每个元素都是真实表或虚拟表。) 如果FROM
列表中指定了多个元素,则它们将交叉连接在一起。 -
如果指定了
WHERE
子句,则从输出中排除所有不满足该条件的行。 -
如果指定了
GROUP BY
子句,或者存在聚合函数调用,则将输出组合为与一个或多个值匹配的行组,并计算聚合函数的结果。如果存在HAVING
子句,它将排除不满足给定条件的组。 -
实际的输出行是使用
SELECT
输出表达式为每个选定的行或行组计算的。 -
SELECT DISTINCT
从结果中消除重复的行。SELECT DISTINCT ON
消除与所有指定表达式匹配的行。SELECT ALL
(默认)将返回所有候选行,包括重复项。 -
如果指定了窗口表达式(和可选的
WINDOW
子句),则根据位置(行)或基于值的(范围)窗口框架来组织输出。 -
使用每个选定行的
SELECT
输出表达式来计算实际的输出行。 -
使用运算符
UNION
,INTERSECT
和EXCEPT
,可以组合多个SELECT
语句的输出以形成单个结果集。
UNION
运算符返回一个或两个结果集中的所有行。INTERSECT
运算符返回完全位于两个结果集中的所有行。EXCEPT
运算符返回第一个结果集中的行,而不是第二个结果集中的行。在所有三种情况下,除非指定了
ALL
,否则将消除重复的行。可以添加噪声字
DISTINCT
来明确指定消除重复行。请注意,
DISTINCT
是此处的默认行为,即使ALL
是SELECT
本身的默认行为。-
如果指定了
ORDER BY
子句,则返回的行将以指定的顺序排序。如果未给出ORDER BY
,则以系统最先找到的顺序返回行。 -
如果指定了
LIMIT
(或FETCH FIRST
)或OFFSET
子句,则SELECT
语句仅返回结果行的子集。 -
如果指定了
FOR UPDATE
,FOR NO KEY UPDATE
,FOR SHARE
或FOR KEY SHARE
,则SELECT
语句将锁定整个表以防止并发更新。
必须对
SELECT
命令中使用的每一列都具有SELECT
权限。 使用FOR NO KEY UPDATE
,FOR UPDATE
,FOR SHARE
或FOR KEY SHARE
也需要UPDATE
权限(对于这样选择的每个表的至少一列)。 -
- 参数
-
该SQL命令参数说明见下
- WITH子句
可选的
WITH
子句允许您指定一个或多个子查询,这些子查询可以在主查询中按名称引用。 子查询在主查询期间有效地充当临时表或视图。每个子查询可以是SELECT
,INSERT
,UPDATE
或DELETE
语句。在用WITH
编写数据修改语句(INSERT
,UPDATE
或DELETE
)时,通常会包含RETURNING
子句。它是RETURNING
的输出,而不是该语句修改的基础表,形成了由主查询读取的临时表。如果省略RETURNING
,则该语句仍将执行,但是不会产生任何输出,因此主查询无法将其引用为表。对于包含
WITH
子句的SELECT
命令,该子句最多可以包含一个用于修改表数据的子句(INSERT
,UPDATE
或DELETE
命令)。必须为
WITH
子句中的每个查询指定一个无schema限定的with_query_name
。(可选)可以指定列名列表;如果省略列名列表,则从子查询中推断出名称。主查询和WITH
查询都(理论上)同时执行。如果指定了
RECURSIVE
,则它允许SELECT
子查询按名称引用自己。 这样的子查询具有一般形式non_recursive_term UNION [ALL | DISTINCT] recursive_term
递归自引用出现在
UNION
的右侧。 每个查询仅允许一个递归自引用。不支持递归数据修改语句,但是您可以在数据修改语句中使用递归SELECT
查询的结果。如果指定了
RECURSIVE
关键字,则无需对WITH
查询进行排序:一个查询可以引用列表中后面的另一个查询。但是,不支持循环引用或相互递归。如果没有
RECURSIVE
关键字, 则WITH
查询只能引用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;
由于集合操作
UNION
没有对CTEfoo
的引用,因此允许此递归CTE。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
中的数据修改语句的效果。如果两个这样的数据修改语句试图修改同一行,则结果不确定。- SELECT列表
SELECT
列表(在关键字SELECT
和FROM
之间)指定用于形成SELECT
语句输出行的表达式。表达式可以(通常)引用FROM
子句中计算的列。SELECT
列表中的expression
可以是常量值,列引用,运算符调用,函数调用,聚合表达式,窗口表达式,标量子查询等。可以将许多结构体归类为表达式,但不遵循任何常规语法规则。 这些通常具有函数或运算符的语义。有关SQL值表达式和函数调用的信息,请参阅SeaboxMPP数据库管理员指南中的“查询数据”。就像在表中一样,
SELECT
的每个输出列都有一个名称。 在简单的SELECT
中,此名称仅用于标记要显示的列,但是当SELECT
是较大查询的子查询时,该名称在较大查询中被视为由该子查询生成的虚拟表的列名。要指定用于输出列的名称,请在该列的表达式之后写入AS
output_name
。(您可以省略AS
,但只有在所需的输出名称与任何SQL关键字都不匹配时才可以使用。为防止将来可能再添加关键字,您始终可以写AS
或在输出名称中用双引号。)如果不指定列名称,SeaboxMPP数据库自动选择一个名称。 如果列的表达式是简单的列引用,则所选名称与该列的名称相同。在更复杂的情况下,可以使用函数或类型名称,或者系统可能会依赖生成的名称(例如?column?
或columnN
)。输出列的名称可用于引用
ORDER BY
和GROUP BY
子句中的列的值, 但不能用于WHERE
或HAVING
子句中。在那里,必须写出表达式。可以将
*
而不是表达式写到输出列表,作为所选行的所有列的简写。 另外,您可以编写table_name.*
作为仅来自该表的列的简写。在这种情况下,无法使用AS
指定新名称。 输出列名称将与表列名称相同。- DISTINCT子句
如果指定了
SELECT DISTINCT
,则将从结果集中删除所有重复的行(每组重复项中保留一行)。SELECT ALL
则相反:所有行都保留;这是默认值。SELECT DISTINCT ON ( expression [, ...] )
仅保留给定表达式等于的每组行的第一行。 使用与ORDER BY
相同的规则来解释DISTINCT ON
表达式(请参见上文)。 请注意,除非使用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
组中所需的行优先级。- FROM子句
FROM
子句为SELECT
指定一个或多个源表。 如果指定了多个源,则结果为所有源的笛卡尔乘积(交叉连接)。但通常会添加限定条件(通过WHERE
),以将返回的行限制为笛卡尔乘积的一小部分。FROM
子句可以包含以下元素:table_name
- 现有表或视图的名称(可以有schema修饰)。 如果指定
ONLY
,则仅扫描该表。如果未指定ONLY
,则扫描该表及其所有子表(如果有)。 alias
- 包含别名的
FROM
项目的替代名称。 别名用于简洁起见或用于消除自连接的歧义(多次扫描同一张表)。提供别名后,它将完全隐藏表或函数的实际名称。例如,在给定FROM foo AS f
的情况下,SELECT
的其余部分必须将此FROM
项目引用为f
而不是foo
。如果写入了别名,则还可以写入列别名列表以为表的一个或多个列提供替代名称。 select
- 子
SELECT
可以出现在FROM
子句中。 这就像在单个SELECT
命令期间将其输出创建为临时表一样。请注意,子SELECT
必须用括号括起来,并且必须为其提供别名。VALUES
命令也可以在这里使用。 with_query_name
- 通过指定其
with_query_name
在FROM
子句中引用with_query
,就像该名称是表名一样。with_query_name
不能包含schema限定符。 可以使用与表相同的方式提供别名。with_query
隐藏用于主查询目的的同名表。 如有必要,可以通过使用schema限定表名来引用相同名称的表。 function_name
-
函数调用可以出现在
FROM
子句中。 (这对于返回结果集的函数特别有用,但是可以使用任何函数。)这就像在单个SELECT
命令期间将其输出创建为临时表一样。 也可以使用别名。如果编写了别名,则还可以编写列别名列表,以为函数的复合返回类型的一个或多个属性提供替代名称。
如果已将函数定义为返回记录数据类型,则必须存在别名或关键字
AS
,然后是形式为(column_name data_type [, ... ] )
的列定义列表。列定义列表必须匹配该函数返回的实际列数和类型。 join_type
-
以下之一:
-
[INNER] JOIN
-
LEFT [OUTER] JOIN
-
RIGHT [OUTER] JOIN
-
FULL [OUTER] JOIN
-
CROSS JOIN
对于
INNER
和OUTER
连接类型,必须指定一个连接条件, 即恰好是NATURAL
,ON join_condition
或USING (join_column[, ...])
之一。 含义如下。 对于CROSS JOIN
,这些子句都不会出现。JOIN子句结合了两个
FROM
项,为方便起见,我们将其称为“表”, 尽管实际上它们可以是任何类型的FROM
项。如有必要,请使用括号确定嵌套顺序。 在没有括号的情况下,JOIN
从左到右嵌套。无论如何,JOIN
的绑定比逗号分隔FROM
列表项的绑定更紧密。CROSS JOIN
和INNER JOIN
产生一个简单的笛卡尔积,与在FROM
的顶层列出两个表所获得的结果相同,但受连接条件(如果有)的限制。CROSS JOIN
等效于INNER JOIN ON
(TRUE)
,即,没有行被限定删除。这些连接类型只是一种符号上的方便,因为它们无法执行普通
FROM
和WHERE
无法完成的工作。LEFT OUTER JOIN
返回合格的笛卡尔乘积中的所有行(即,所有通过其连接条件的组合行),加上左表中没有通过右表连接条件的每行的一个副本。通过为右侧列插入空值,此左侧行将扩展为连接表的整个宽度。请注意,在确定哪些行具有匹配项时,仅考虑JOIN
子句自身的条件。之后应用外部条件。相反,
RIGHT OUTER JOIN
返回所有已连接的行,并为每个不匹配的右手行加上一行(在左边扩展为空)。这只是一种符号上的便利,因为您可以通过切换左右表将其转换为LEFT OUTER JOIN
。FULL OUTER JOIN
返回所有连接的行,再加上一行,用于每行不匹配的左表行(在右边扩展为空),再加上每行不匹配的右表行(扩展在左边为空)。 -
- ON
join_condition
join_condition
是一个表达式,其结果为boolean
类型的值(类似于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
。
- WHERE子句
可选的
WHERE
子句具有一般形式:WHERE condition
condition
是任何计算结果为boolean
型结果的表达式。 任何不满足此条件的行将从输出中删除。如果用一行的实际值替换其中的变量引用后,该表达式返回真,则该行符合条件。-
GROUP BY子句
可选的
GROUP BY
子句具有一般形式:GROUP BY grouping_element[, ...]
其中
grouping_element
可以是以下之一:sql () expression ROLLUP (expression [,...]) CUBE (expression [,...]) GROUPING SETS ((grouping_element [, ...]))
GROUP BY
将所有共享有相同值的分组表达式的所有选定行压缩为单行。expression
可以是输入列名称,也可以是输出列(SELECT
列表项)的名称或序号,或者是由输入列值组成的任意表达式。如有歧义,GROUP BY
名称将被解释为输入列名称,而不是输出列名称。汇总函数(如果有的话)在组成每个组的所有行中进行计算,从而为每个组生成单独的值。 (如果有聚合函数但没有
GROUP BY
子句,则该查询被视为具有包含所有选定行的单个组。)可以通过将FILTER
子句附加到聚合函数调用来进一步过滤提供给每个聚合函数的行集。如果存在FILTER
子句,则只有与之匹配的那些行才包含在该聚合函数的输入中。如果存在
GROUP BY
或任何聚合函数,则SELECT
列表表达式不能引用未分组的列,除非是在聚合函数内,或者当未分组的列在函数上依赖于分组的列时,否则对于未分组的列将会返回不止一个的可能的值。如果分组的列(或其子集)是包含未分组的列的表的主键,则存在函数依赖。请记住,在评估
HAVING
子句或SELECT
列表中的任何“标量”表达式之前,将评估所有聚合函数。SeaboxMPP数据库具有以下附加的OLAP分组扩展(通常称为supergroups):
- 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 [, …]) — 可以将
grouping
函数应用于一个或多个分组属性,以将超聚合的行与常规的分组的行区分开。这有助于将代表超级汇总行中所有值的集合的“NULL”与常规行中的NULL
值区分开。此函数中的每个参数都产生一个位 -1
或0
, 其中1
表示结果行是超级聚合的,而0
表示结果行来自常规分组。grouping
函数通过将这些位视为二进制数然后将其转换为以10为基的整数来返回整数。 -
group_id() — 对于包含重复分组集的分组扩展查询,
group_id
函数用于标识输出中的重复行。所有唯一分组集输出的唯一行的group_id值为0。对于每个检测到的重复分组集,group_id
函数分配的group_id编号大于0。特定重复分组集中的所有输出行均由相同的group_id编号标识。
- 注意,
- WINDOW子句
可选的
WINDOW
子句指定出现在查询的SELECT
列表或ORDER BY
子句中的窗口函数的行为。这些函数可以在其OVER
子句中按名称引用WINDOW
子句条目。但是,WINDOW
子句条目不必在任何地方引用。 如果查询中未使用它,则将其忽略。可以根本不使用任何WINDOW
子句来使用窗口函数,因为窗口函数调用可以直接在其OVER
子句中指定其窗口定义.但是,当多个窗口函数需要相同的窗口定义时,WINDOW
子句将保存键入内容。例如:
SELECT vendor, rank() OVER (mywindow) FROM sale GROUP BY vendor WINDOW mywindow AS (ORDER BY sum(prc*qty));
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
- 如果指定了
existing_window_name
,则它必须引用WINDOW
列表中的较早条目;新窗口将复制该条目的分区子句以及其排序子句(如果有)。新窗口不能指定自己的PARTITION BY
子句,只有复制的窗口没有该子句时,它才能指定ORDER BY
。 新窗口始终使用其自己的frame子句;复制的窗口不得指定frame子句。 - PARTITION BY
-
PARTITION BY
子句根据指定表达式的唯一值将结果集组织为逻辑组。PARTITION BY
子句的元素的解释方式与GROUP BY
子句的元素几乎相同,不同之处在于它们始终是简单表达式,而不是输出列的名称或编号。另一个区别是这些表达式可以包含聚合函数调用,这在常规GROUP BY
子句中是不允许的。 在此处允许使用它们,因为窗口在分组和聚合之后发生。与窗口函数一起使用时,这些函数将分别应用于每个分区。
例如,如果在
PARTITION BY
之后加上列名,则结果集将按该列的不同值进行分区。 如果省略,则将整个结果集视为一个分区。同样,
ORDER BY
列表中的元素的解释方式与ORDER BY
子句中的元素几乎相同,不同之处在于,始终将表达式视为简单表达式,而不使用输出列的名称或编号。 - ORDER BY
ORDER BY
子句的元素定义如何对结果集的每个分区中的行进行排序。 如果省略,则以最有效的顺序返回行,并且行可能会有所不同。注意:缺少一致顺序(例如time
)的数据类型的列不是在窗口规范的ORDER BY
子句中使用的良好候选对象。带有或不带有时区的时间缺乏连贯的排序,因为加法和减法没有预期的效果。 例如,以下条件通常不成立:x::time < x::time + '2 hour'::interval
frame_clause
-
可选的
frame_clause
定义依赖于框架的窗口函数的窗口框架(并非全部如此)。窗口框架是查询的每一行(称为当前行)的一组相关行。frame_clause
可以是以下之一{ RANGE | ROWS } frame_start { RANGE | ROWS } BETWEEN frame_start AND frame_end
其中
frame_start
和frame_end
可以是以下之一:-
UNBOUNDED PRECEDING
-
value PRECEDING
-
CURRENT ROW
-
value FOLLOWING
-
UNBOUNDED FOLLOWING
如果省略
frame_end
,则默认为CURRENT ROW
。 限制条件是frame_start
不能为UNBOUNDED FOLLOWING
,frame_end
不能为UNBOUNDED PRECEDING
,并且frame_end
选择不能在上述列表中出现在frame_start
选择之前 — 例如,RANGE BETWEEN CURRENT ROW AND value PRECEDING
是不允许的。默认的框架选项是
RANGE UNBOUNDED PRECEDING
,与RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
之间的范围相同;它将框架设置为从分区开始到当前行的最后一个对等方的所有行(ORDER BY
的行认为与当前行等效,如果没有ORDER BY
则为所有行)。 通常,UNBOUNDED PRECEDING
表示框架以分区的第一行开始, 类似地UNBOUNDED FOLLOWING
表示框架以分区的最后一行结束(与RANGE
或ROWS
模式无关)。在
ROWS
模式下,CURRENT ROW
表示框架以当前行开始或结束;但是在RANGE
模式下,这意味着框架以ORDER BY
顺序中当前行的第一个或最后一个对等点开始或结束。当前仅在
ROWS
模式下允许value
PRECEDING
和value
FOLLOWING
情况。 它们指示框架以当前行之前或之后许多行的行开始或结束。value
必须是不包含任何变量,聚合函数或窗口函数的整数表达式。该值不能为null或负数;但它可以为零,从而选择当前行本身。请注意,如果
ORDER BY
排序不能唯一地对行进行排序,则ROWS
选项可能会产生不可预测的结果。RANGE
选项旨在确保以相同的方式对待在ORDER BY
顺序中为对等的行;所有对等行将在同一框架中。使用
ROWS
或RANGE
子句来表示窗口的边界。 窗口边界可以是一个分区的一行,多行或所有行。可以用偏移当前行值的数据值范围(RANGE
)或偏移当前行的行数(ROWS
)来表示窗口的边界。使用RANGE
子句时,还必须使用ORDER BY
子句。 这是因为为生成窗口而执行的计算需要对值进行排序。 此外,ORDER BY
子句不能包含多个表达式,并且该表达式必须导致日期或数字值。使用ROWS
或RANGE
子句时,如果仅指定起始行,则当前行将用作窗口中的最后一行。PRECEDING —
PRECEDING
子句使用当前行作为参考点定义窗口的第一行。 起始行以当前行之前的行数表示。例如,对于ROWS
框架,5 PRECEDING
设置窗口从当前行之前的第五行开始。对于RANGE
框架,它将窗口设置为从第一行开始,其排序列值在给定顺序比当前行的顺序高5。如果指定的顺序按日期升序,则它将是当前行之前5天内的第一行。UNBOUNDED PRECEDING
将窗口中的第一行设置为分区中的第一行。BETWEEN —
BETWEEN
子句使用当前行作为参考点定义窗口的第一行和最后一行。第一行和最后一行分别用当前行之前和之后的行数表示。例如,BETWEEN 3 PRECEDING AND 5 FOLLOWING
将窗口设置为从当前行之前的第三行开始,到当前行之后的第五行结束。使用BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
,可以将窗口中的第一行和最后一行分别设置为分区的第一行和最后一行。如果未指定ROW
或RANGE
子句,则等效于默认行为。FOLLOWING — FOLLOWING子句使用当前行作为参考点定义窗口的最后一行。 最后一行以当前行之后的行数表示。例如,对于
ROWS
框架,5 FOLLOWING
将窗口设置为在当前行之后的第五行结束。在RANGE
框架的情况下,它将窗口设置为最后一行的末尾,其排序列值在给定顺序的当前行后5个。如果指定的顺序按日期升序,则它将是当前行之后5天内的最后一行。 使用UNBOUNDED FOLLOWING
将窗口中的最后一行设置为分区中的最后一行。如果未指定
ROW
或RANGE
子句,并且使用ORDER BY
, 则窗口绑定从分区的第一行(UNBOUNDED PRECEDING
)开始,到当前行(CURRENT ROW
)结束。 如果未指定ORDER BY
,则窗口从分区的第一行开始(UNBOUNDED PRECEDING
),然后从分区的最后一行结束(UNBOUNDED FOLLOWING
)。 -
- HAVING子句
可选的
HAVING
子句具有以下一般形式:HAVING condition
其中
condition
与为WHERE
子句指定的条件相同。HAVING
去除不满足条件的组行。HAVING
与WHERE
不同:WHERE
在应用GROUP BY
之前过滤单个行,而HAVING
过滤GROUP BY
创建的组行。condition
中引用的每个列都必须明确引用一个分组列,除非该引用出现在聚合函数中或未分组的列在函数上依赖于分组列。即使没有
GROUP BY
子句,HAVING
的存在也会将查询转换为分组查询。 这与查询包含聚合函数但不包含GROUP BY
子句的情况相同。所有选定的行都被视为形成一个单一的组,并且SELECT
列表和HAVING
子句只能引用聚合函数中的表列。如果HAVING
条件为true,则此类查询将返回单行;如果条件不是true,则将返回零行。- 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 BY
和LIMIT
括在圆括号中,则可以将其附加到子查询表达式。不带圆括号,这些子句将应用于UNION
的结果,而不是其右侧输入表达式。)UNION
运算符计算所涉及的SELECT
语句返回的行的集合并集。 如果行出现在至少一个结果集中,则在两个结果集中的行并集中。表示UNION
的直接操作数的两个SELECT
语句必须产生相同数量的列,并且对应的列必须具有兼容的数据类型。除非指定了
ALL
选项,否则UNION
的结果不包含任何重复的行。ALL
防止重复项的消除。 (因此,UNION ALL
通常比UNION
快得多;请尽可能使用ALL
。) 可以编写DISTINCT
来明确指定消除重复行的默认行为。除非括号中另有说明,否则同一
SELECT
语句中的多个UNION
运算符从左到右求值。当前,不能为
UNION
结果或UNION
的任何输入指定FOR NO KEY UPDATE
,FOR UPDATE
,FOR SHARE
和FOR KEY SHARE
。- 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)
。当前,不能为
INTERSECT
结果或INTERSECT
的任何输入指定FOR NO KEY UPDATE
,FOR UPDATE
,FOR SHARE
和FOR KEY SHARE
。- 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
语句的结果但不在右SELECT
的结果的行集。除非指定了
ALL
选项,否则EXCEPT
的结果将不包含任何重复的行。使用ALL
,在左表中具有m
个重复项且在右表中具有n
个重复项的行将在结果集中出现max(m-n
,0)次。可以编写DISTINCT
来明确指定消除重复行的默认行为。除非括号中另有规定,否则同一
SELECT
语句中的多个EXCEPT
运算符从左到右求值。EXCEPT
与UNION
优先级相同。当前,不能为
EXCEPT
结果或EXCEPT
的任何输入指定FOR NO KEY UPDATE
,FOR UPDATE
,FOR SHARE
和FOR KEY SHARE
。- ORDER BY子句
可选的
ORDER BY
子句具有以下一般形式:ORDER BY expression [ASC | DESC | USING operator] [NULLS {FIRST | LAST}] [,...]
其中
expression
可以是输出列(SELECT
列表项)的名称或序号, 也可以是由输入列值组成的任意表达式。ORDER BY
子句使结果行根据指定的表达式排序。 如果两行最左边的表达式相等,则根据下一个表达式对它们进行比较,依此类推。如果所有指定的表达式相等,则将根据实现的顺序返回它们。序号是指输出列的序数位置(从左到右)。 通过此特性,可以基于没有唯一名称的列来定义顺序。这绝对不是必须的,因为始终可以使用
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 DESC
的含义与ORDER BY x DESC,y DESC
的含义不同。字符串数据是根据创建数据库时建立的特定于语言环境的排序顺序进行排序的。
字符串数据是根据应用于要排序的列的排序规则进行排序的。 可以根据需要通过在
expression
中包含COLLATE
子句来覆盖它,例如ORDER BY mycolumn COLLATE "en_US"
。 有关定义排序规则的信息,请参见CREATE COLLATION
。- LIMIT子句
LIMIT
子句包含两个独立的子句:LIMIT {count | ALL} OFFSET start
其中
count
指定要返回的最大行数,而start
指定在开始返回行之前要跳过的行数。如果同时指定了两者,则在开始对要返回的count
行进行计数之前,将跳过起始行。如果
count
表达式的计算结果为NULL,则将其视为LIMIT ALL
,即无限制。如果start
计算为NULL,则将其与OFFSET 0
相同。SQL:2008引入了不同的语法来实现相同的结果,SeaboxMPP数据库也支持该语法。 它是:
OFFSET start [ ROW | ROWS ] FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY
在这种语法中,标准要求
start
或count
值是文字常量,参数或变量名;作为SeaboxMPP数据库扩展,可以使用其他表达式,但通常需要将其括在括号中以避免歧义。如果在FETCH
子句中省略了count
,则其默认值为1。ROW
和ROWS
以及FIRST
和NEXT
是不影响这些子句效果的干扰词。根据标准,如果两者都存在,则OFFSET
子句必须位于FETCH
子句之前。 但SeaboxMPP数据库允许使用任何顺序。使用
LIMIT
时,最好使用ORDER BY
子句将结果行强制为唯一的顺序。 否则,您将获得查询行的不可预测的子集 - 可能会要求以第十到第二十行,但以什么顺序要求第十到第二十行? 除非您指定ORDER Y
,否则您不知道该如何排序。查询优化器在生成查询计划时会考虑
LIMIT
,因此根据LIMIT
和OFFSET
的使用方式,您很可能会获得不同的计划(产生不同的行顺序)。因此,除非使用ORDER BY
强制执行可预测的结果顺序,否则使用不同的LIMIT/OFFSET
值选择查询结果的不同子集将产生不一致的结果。这不是缺陷;这是一个正常的结果,即除非使用ORDER BY
强制该顺序,否则SQL不会保证以任何特定顺序传递查询结果。- Locking子句
FOR UPDATE
,FOR NO KEY UPDATE
,FOR SHARE
和FOR KEY SHARE
是锁定子句;它们会影响SELECT
如何锁定从表中获取的行。锁定子句具有一般形式
FOR lock_strength [OF table_name [ , ... ] ] [ NOWAIT ]
其中
lock_strength
可以是以下之一UPDATE NO KEY UPDATE SHARE KEY SHARE
注意:默认情况下,SeaboxMPP数据库获取对表的
EXCLUSIVE
锁,以对堆表进行DELETE
和UPDATE
操作。启用全局死锁检测器后,堆表上DELETE
和UPDATE
操作的锁定模式为ROW EXCLUSIVE
。通过将sc_enable_global_deadlock_detector配置参数设置为true来启用全局死锁检测器。为防止该操作等待其他事务提交,请使用
NOWAIT
选项。 使用NOWAIT
,如果无法立即锁定选定的行,该语句将报告错误,而不是等待。请注意,NOWAIT
仅适用于行级锁 - 所需的ROW SHARE
表级锁仍以常规方式获取。如果需要不等待就获取表级锁,则可以先将LOCK与NOWAIT
选项一起使用。如果在锁定子句中命名了特定的表,则仅锁定来自那些表的行;其他使用
SELECT
的表则像往常一样简单地读取。没有表列表的锁定子句会影响该语句中使用的所有表。如果将锁定子句应用于视图或子查询,则它将影响视图或子查询中使用的所有表。但是,这些子句不适用于主查询引用的WITH
查询。如果希望在WITH
查询中发生行锁定,请在WITH
查询中指定一个锁定子句。如果有必要为不同的表指定不同的锁定行为,则可以编写多个锁定子句。如果同一表被两个以上的锁定子句提及(或隐式影响),则将其视为仅由最强的一个子表指定。同样,如果在影响该表的任何子句中指定了表,则该表将作为
NOWAIT
处理。在无法用单独的表行清楚地标识返回的行的上下文中,不能使用锁定子句;例如,它们不能与聚合一起使用。
当锁定子句出现在
SELECT
查询的顶层时,被锁定的行与查询返回的行完全相同。 对于连接查询,锁定的行是那些促成返回连接行的行。此外,查询快照中满足查询条件的行将被锁定,尽管这些行在快照后对其进行更新并且不再满足查询条件不会返回。如果使用LIMIT
,则一旦返回了足够的行以满足限制,锁定就会停止(但请注意,被OFFSET
跳过的行将被锁定)。同样,如果在游标的查询中使用锁定子句,则仅锁定实际被游标读取或跳过的行。当sub-
SELECT
中出现locking子句时,被锁定的行就是该子查询返回给外部查询的行。这可能涉及的行数少于单独检查子查询所建议的行数,因为外部查询的条件可用于优化子查询的执行。例如,
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;
将仅锁定
col1 = 5
的行,即使该条件在文本上不在子查询中。SELECT
命令可能以READ COMMITTED
事务隔离级别运行,并且使用ORDER BY
和锁定子句可以使行无序返回。这是因为ORDER BY
首先被应用。 该命令对结果进行排序,但随后可能会阻止尝试获得对一个或多个行的锁定。一旦SELECT
解除阻塞,某些排序列值可能已被修改,从而导致这些行看起来是乱序的(尽管就原始列值而言,它们是有序的)。例如,可以通过在子查询中放置
FOR UPDATE/SHARE
子句来解决此问题。SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1;
请注意,这将导致锁定
mytable
的所有行,而顶层的FOR UPDATE
将仅锁定实际返回的行。这可能会导致明显的性能差异,特别是如果将ORDER BY
与LIMIT
或其他限制结合使用。 因此,仅当期望并发更新排序列并且需要严格排序的结果时才建议使用此技术。在
REPEATABLE READ
或SERIALIZABLE
事务隔离级别,这将导致序列化失败(SQLSTATE
为40001
),因此在这些隔离级别下,不可能乱序接收行。 - 示例
-
连接表
films
与表distributors
:SELECT f.title, f.did, d.name, f.date_prod, f.kind FROM distributors d, films f WHERE f.did = d.did
将表films的
length
列求和并将结果按kind
分组:SELECT kind, sum(length) AS total FROM films GROUP BY kind;
将表films的
length
列求和并将结果按kind
分组并显示总和小于5小时的组:SELECT kind, sum(length) AS total FROM films GROUP BY kind HAVING sum(length) < interval '5 hours';
计算电影
kind
和distributor
的所有销售部分和与总和。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 sale GROUP BY distributor ORDER BY 2 DESC;
以下两个示例是根据第二列(
name
)的内容对单个结果进行排序的相同方法: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);
此示例使用一个简单的
WITH
子句: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
子句,但需要两级嵌套的sub-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]
,然后是查询的递归部分。确保查询的递归部分最终不会返回任何元组,否则查询将无限期地循环。 - 兼容性说明
-
SELECT
语句与SQL标准兼容,但是有一些扩展和某些缺少的功能。- 省略FROM子句
SeaboxMPP数据库允许省略
FROM
子句。 它可以直接用于计算简单表达式的结果。例如:SELECT 2+2;
其他一些SQL数据库无法做到这一点,除非引入一个虚拟的单行表来执行
SELECT
。请注意,如果未指定
FROM
子句,则查询无法引用任何数据库表。 例如,以下查询无效:SELECT distributors.* WHERE distributors.name = 'Westward';
在较早的版本中,将服务器配置参数
add_missing_from
设置为true允许SeaboxMPP数据库为查询所引用的每个表向查询的FROM
子句添加隐式条目。这个不再允许。- 省略AS关键字
在SQL标准中,只要新列名是有效的列名(即与任何保留关键字不同),就可以在输出列名之前省略可选关键字
AS
。SeaboxMPP数据库的限制更为严格:如果新列名完全匹配任何关键字(保留与否),则要求使用AS
。推荐的做法是使用AS
或双引号输出列名,以防止与将来添加关键字的任何可能的冲突。在
FROM
项中,SQL标准和SeaboxMPP数据库都允许在作为未保留关键字的别名之前省略AS
。但是由于语法上的歧义,这对于输出列名称是不切实际的。- ONLY和Inheritance
使用
ONLY
时,SQL标准要求在表名前后加上括号,例如:SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...
SeaboxMPP数据库认为这些括号是可选的。
SeaboxMPP数据库允许编写尾随
*
来明确指定包括子表的non-ONLY
行为。 该标准不允许这样做。这些要点同样适用于所有支持ONLY
选项的SQL命令。- Namespace可以用于GROUP BY和ORDER BY
在SQL-92标准中,
ORDER BY
子句只能使用输出列名称或序号,而GROUP BY
子句只能使用基于输入列名称的表达式。SeaboxMPP数据库扩展了这些子句中的每一个,以允许其他选择(但是如果有歧义,它将使用标准的解释)。SeaboxMPP数据库还允许两个子句都指定任意表达式。请注意,出现在表达式中的名称始终被视为输入列名称,而不是输出列名称。SQL:1999及更高版本使用的定义略有不同,但并不完全与SQL-92向上兼容。但是,在大多数情况下,SeaboxMPP数据库以与SQL:1999相同的方式解释
ORDER BY
或GROUP BY
表达式。- 函数依赖
仅当表的主键包含在
GROUP BY
列表中时,SeaboxMPP数据库才能识别函数依赖(允许从GROUP BY
省略列)。SQL标准指定了应识别的其他条件。- LIMIT和OFFSET
子句
LIMIT
和OFFSET
是SeaboxMPP数据库特定的语法,也由MySQL使用。如上所述,SQL:2008标准引入了子句OFFSET .. FETCH {FIRST | NEXT} ...
来实现相同的功能。 IBM DB2也使用此语法。(Oracle应用程序经常使用一种变通办法来实现这些子句的效果,该变通办法涉及自动生成的rownum
列,SeaboxMPP数据库中不提供该列。)- FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE和FOR KEY SHARE
尽管
FOR UPDATE
出现在SQL标准中,但该标准仅允许将它作为DECLARE CURSOR
的选项。SeaboxMPP数据库允许在任何SELECT
查询以及sub-SELECT
中使用它,但这是一个扩展。FOR NO KEY UPDATE
,FOR SHARE
和FOR KEY SHARE
变体以及NOWAIT
选项未出现在标准中。- WITH中的数据修改语句
SeaboxMPP数据库允许将
INSERT
,UPDATE
和DELETE
用作WITH
查询。 在SQL标准中不允许。- 非标准子句
在SQL标准中未定义
DISTINCT ON
子句。- STABLE和VOLATILE函数的限制
为防止数据在SeaboxMPP数据库中的各个executor之间变得不同步,如果分类为
STABLE
或VOLATILE
的任何函数包含SQL或以任何方式修改了数据库,则不能在executor数据库级别执行该函数。有关更多信息,请参见CREATE FUNCTION
。 - 相关SQL命令
EXPLAIN