表表达式用于计算表。表表达式包含一个子句FROM,该子句后跟 WHERE、GROUP BY和HAVING子句(可选)。普通表表达式仅指磁盘上的表,即所谓的基表,但可以使用更复杂的表达式以各种方式修改或组合基表。
表表达式中的可选WHERE 、GROUP BY 和 HAVING子句指定FROM对子句中派生的表执行的连续转换的管道。所有这些转换都会生成一个虚拟表,该表提供传递给选择列表的行,以计算查询的输出行。
FROM 子句从逗号分隔的表引用列表中给出的一个或多个其他表派生表。
FROM table_reference [, table_reference [, ...]]
表引用可以是表名(可能是架构限定的),也可以是派生表,例如子查询、构造或它们的复杂组合。如果子句中列出了多个表引用,则这些表将交叉连接(即JOIN,形成其行的笛卡尔积;见下文)。列表的结果是一个中间虚拟表,然后可以通过 WHEREGROUP BY和HAVING子句进行转换,最后是整个表表达式的结果。
当表引用命名的表是表继承层次结构的父级时,表引用不仅会生成该表的行,还会生成其所有后代表的行,除非关键字ONLY位于表名前面。但是,引用仅生成出现在命名表中的列,而子表中添加的任何列都将被忽略。
您可以在表名之后写入表名,而不是在表名之前写入,以显式指定包含后代表。没有真正的理由再使用此语法,因为搜索后代表现在始终是默认行为。但是,它支持与旧版本兼容。
联接表是根据特定联接类型的规则从另外两个(实数或派生)表派生的表。提供内部连接、外部连接和交叉连接。联接表的一般语法是
T1 join_type T2 [ join_condition ]
T1 CROSS JOIN T2
对于 T1 和 T2 中行的每种可能组合(即笛卡尔积),联接表将包含一行,该行由 T1 中的所有列组成,后跟 T2 中的所有列。如果表分别有 N 行和 M 行,则联接表将有 N * M 行。
FROM T1 CROSS JOIN T2相当于(见下文)。它也等价于 。FROM T1 INNER JOIN T2 ON TRUEFROM T1, T2
注意:当出现两个以上的表时,后一种等价关系并不完全成立,因为绑定比逗号更紧密。例如,与条件在第一种情况下可以引用 T1
而在第二种情况下不能引用 T1 不同。JOINFROM T1 CROSS JOIN T2 INNER JOIN T3 ON
conditionFROM T1, T2 INNER JOIN T3 ON condition
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON boolean_expression
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( join column list )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2
单词 和 在所有形式中都是可选的。 是默认值;、 和 表示外部连接。INNEROUTERINNERLEFTRIGHTFULL
连接条件在ON or USING子句中指定,或由单词NATURAL隐式指定。联接条件确定两个源表中的哪些行被视为“匹配”,如下所述。
合格联接的可能类型包括:
INNER JOIN
对于 T1 的每一行 R1,联接表对应 T2 中满足 R1 联接条件的每一行都有一行。
LEFT OUTER JOIN
首先,执行内部连接。然后,对于 T1 中不满足 T2 中任何行的联接条件的每一行,将在 T2 的列中添加一个具有 null 值的联接行。因此,联接表在 T1 中的每一行始终至少有一行。
RIGHT OUTER JOIN
首先,执行内部连接。然后,对于 T2 中不满足与 T1 中任何行的联接条件的每一行,将在 T1 的列中添加一个具有 null 值的联接行。这与左连接相反:结果表将始终对应 T2 中的每一行。
FULL OUTER JOIN
首先,执行内部连接。然后,对于 T1 中不满足 T2 中任何行的联接条件的每一行,将在 T2 的列中添加一个具有 null 值的联接行。此外,对于不满足 T1 中任何行的联接条件的 T2 的每一行,将添加一个在 T1 的列中具有 null 值的联接行。
子句是最通用的连接条件:它采用与子句中使用的相同类型的布尔值表达式。如果表达式的计算结果为 true,则 T1 和 T2 中的一对行匹配。ONWHEREON
该子句是一种速记,允许您利用联接两端对联接列使用相同名称的特定情况。它采用以逗号分隔的共享列名列表,并形成一个联接条件,其中包括每个列名的相等性比较。例如,将 T1 和 T2 联接会产生联接条件。USINGUSING (a, b)ON T1.a = T2.a AND T1.b = T2.b
此外,输出抑制了冗余列:不需要打印两个匹配的列,因为它们必须具有相等的值。虽然生成 T1 中的所有列,然后生成 T2 中的所有列,则为列出的每个列对生成一个输出列(按列出的顺序),然后是 T1 中的任何剩余列,然后是 T2 中的任何剩余列。JOIN USINGJOIN ONJOIN USING
最后,NATURAL是USING的简写形式:它形成一个列表,USING由两个输入表中出现的所有列名组成。USING与NATURAL 一样,这些列在输出表中仅出现一次。如果没有通用列名,则其行为类似于 ON TRUE,生成跨产品联接。
注意:USING由于仅合并列出的列,因此对连接关系中的列更改相当安全。
风险要大得多,因为对任一关系的任何架构更改都会导致存在新的匹配列名称,也会导致联接合并该新列。
为了把这些放在一起,假设我们有表格:t1
num | name
-----+------
1 | a
2 | b
3 | c
和:t2
num | value
-----+-------
1 | xxx
3 | yyy
5 | zzz
然后,我们得到各种联接的以下结果:
=> SELECT * FROM t1 CROSS JOIN t2;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
1 | a | 3 | yyy
1 | a | 5 | zzz
2 | b | 1 | xxx
2 | b | 3 | yyy
2 | b | 5 | zzz
3 | c | 1 | xxx
3 | c | 3 | yyy
3 | c | 5 | zzz
(9 rows)
=> SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
3 | c | 3 | yyy
(2 rows)
=> SELECT * FROM t1 INNER JOIN t2 USING (num);
num | name | value
-----+------+-------
1 | a | xxx
3 | c | yyy
(2 rows)
=> SELECT * FROM t1 NATURAL INNER JOIN t2;
num | name | value
-----+------+-------
1 | a | xxx
3 | c | yyy
(2 rows)
=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | 3 | yyy
(3 rows)
=> SELECT * FROM t1 LEFT JOIN t2 USING (num);
num | name | value
-----+------+-------
1 | a | xxx
2 | b |
3 | c | yyy
(3 rows)
=> SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
3 | c | 3 | yyy
| | 5 | zzz
(3 rows)
=> SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | 3 | yyy
| | 5 | zzz
(4 rows)
指定的联接条件也可以包含与联接不直接相关的条件。这对于某些查询很有用,但需要仔细考虑。例如:ON
=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | |
(3 rows)
请注意,将限制放在子句中会产生不同的结果:WHERE
=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
(1 row)
这是因为子句中的限制在联接之前处理,而子句中的限制在联接之后处理。这对于内部连接无关紧要,但对于外部连接来说很重要。
可以为表和复杂表引用指定一个临时名称,以用于在查询的其余部分引用派生表。这称为表别名。
要创建表别名,请将
FROM table_reference AS alias
或
FROM table_reference alias
关键词是可选噪音。alias 可以是任何标识符。AS
表别名的典型应用是将短标识符分配给长表名,以保持连接子句的可读性。例如:
SELECT * FROM some_very_long_table_name s JOIN another_fairly_long_name a ON s.id = a.num;
就当前查询而言,别名将成为表引用的新名称 - 不允许在查询中的其他位置使用原始名称引用表。因此,这是无效的:
SELECT * FROM my_table AS m WHERE my_table.a > 5; -- wrong
表别名主要是为了符号的方便,但在将表连接到自身时必须使用它们,例如:
SELECT * FROM people AS mother JOIN people AS child ON mother.id = child.mother_id;
括号用于解决歧义。在以下示例中,第一个语句将别名分配给 的第二个实例,但第二个语句将别名分配给连接的结果:bmy_table
SELECT * FROM my_table AS a CROSS JOIN my_table AS b ...
SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ...
另一种形式的表别名为表的列以及表本身提供临时名称:
FROM table_reference [AS] alias ( column1 [, column2 [, ...]] )
如果指定的列别名少于实际表的列数,则不会重命名其余列。此语法对于自联接或子查询特别有用。
当别名应用于子句的输出时,该别名会隐藏 .例如:JOINJOIN
SELECT a.* FROM my_table AS a JOIN your_table AS b ON ...
是有效的 SQL,但是:
SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c
无效;表别名在别名之外不可见。
指定派生表的子查询必须括在括号中。可以为它们分配一个表别名,以及可选的列别名)。例如:
FROM (SELECT * FROM table1) AS alias_name
此示例等效于 。当子查询涉及分组或聚合时,会出现更有趣的情况,这些情况不能简化为普通连接。FROM table1 AS alias_name
子查询也可以是列表:VALUES
FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow'))
AS names(first, last)
同样,表别名是可选的。将别名分配给列表的列是可选的,但这是一种很好的做法。
根据 SQL 标准,必须为子查询提供表别名。PostgreSQL 允许省略别名,但在可能移植到另一个系统的 SQL 代码中编写一个别名是很好的做法。AS
表函数是生成一组行的函数,这些行由基本数据类型(标量类型)或复合数据类型(表行)组成。它们在查询的子句中像表、视图或子查询一样使用。表函数返回的列可以包含在 、 或子句中,其方式与表、视图或子查询的列相同。FROMSELECTJOINWHERE
表函数也可以使用语法进行组合,结果以并行列的形式返回;在本例中,结果行数是最大函数结果的行数,较小的结果填充了要匹配的 null 值。ROWS FROM
function_call [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]
ROWS FROM( function_call [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]
如果指定了该子句,则会在函数结果列中添加一个附加的 type 列。此列对函数结果集的行进行编号,从 1 开始。(这是 的 SQL 标准语法的概括。默认情况下,序号列称为 ,但可以使用子句为其分配不同的列名。WITH ORDINALITY bigint UNNEST … WITH ORDINALITY ordinality AS
可以使用任意数量的数组参数调用特殊表函数UNNEST ,并返回相应数量的列,就好像已分别调用每个参数并使用构造进行组合一样。UNNEST ROWS FROM
UNNEST( array_expression [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]
如果未指定table_alias,则使用函数名作为表名;在构造的情况下,使用第一个函数的名称。ROWS FROM()
如果未提供列别名,则对于返回基数据类型的函数,列名也与函数名相同。对于返回复合类型的函数,结果列获取该类型的各个属性的名称。
CREATE TABLE foo (fooid int, foosubid int, fooname text);
CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
SELECT * FROM foo
WHERE foosubid IN (
SELECT foosubid
FROM getfoo(foo.fooid) z
WHERE z.fooid = foo.fooid
);
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
在某些情况下,定义表函数很有用,这些函数可以根据调用方式返回不同的列集。为了支持这一点,可以将 table 函数声明为返回不带参数的伪类型。在查询中使用此类函数时,必须在查询本身中指定预期的行结构,以便系统知道如何解析和规划查询。此语法如下所示:record OUT
function_call [AS] alias (column_definition [, ... ])
function_call AS [alias] (column_definition [, ... ])
ROWS FROM( ... function_call AS (column_definition [, ... ]) [, ... ] )
不使用语法时,column_definition列表将替换本来可以附加到项目的列别名列表;列定义中的名称用作列别名。使用语法时,可以分别将column_definition列表附加到每个成员函数;或者,如果只有一个成员函数而没有子句,则可以编写一个 column_definition 列表来代替后面的列别名列表。ROWS FROM() FROM ROWS FROM() WITH ORDINALITYROWS FROM()
SELECT *
FROM dblink('dbname=mydb', 'SELECT proname, prosrc FROM pg_proc')
AS t1(proname name, prosrc text)
WHERE proname LIKE 'bytea%';
dblink 函数(dblink 模块的一部分)执行远程查询。它被声明为返回,因为它可用于任何类型的查询。必须在调用查询中指定实际的列集,以便分析器知道,例如record*,应该扩展到什么。
此示例使用:ROWS FROM
SELECT *
FROM ROWS FROM
(
json_to_recordset('[{"a":40,"b":"foo"},{"a":"100","b":"bar"}]')
AS (a INTEGER, b TEXT),
generate_series(1, 3)
) AS x (p, q, s)
ORDER BY p;
p | q | s
-----+-----+---
40 | foo | 1
100 | bar | 2
| | 3
它将两个函数合并为一个目标。 指示返回两列,第一列和第二列。结果直接使用。该子句将列值排序为整数。
中出现的子查询可以以关键字FROM LATERAL开头。这允许它们引用前面项提供的列。(如果没有 ,则每个子查询都是独立计算的,因此不能交叉引用任何其他项。
中出现的表函数也可以在关键字前面加上关键字,但对于函数,关键字是可选的;在任何情况下,函数的参数都可以包含对前面各项提供的列的引用。
项目可以显示在列表的顶层,也可以显示在树中。在后一种情况下,它也可以指位于右侧左侧的任何项目。
当项目包含交叉引用时,计算过程如下:对于提供交叉引用列的项的每一行,或提供列的多个项的行集,使用该行或行集的列值来评估项目。生成的行像往常一样与计算它们的行联接。对列源表中的每行或每组行重复此操作。
一个简单的例子是LATERAL
SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;
这不是特别有用,因为它与更传统的结果完全相同
SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;
LATERAL当需要交叉引用的列来计算要联接的行时,主要有用。一个常见的应用程序是为集合返回函数提供参数值。例如,假设返回多边形的顶点集,我们可以使用以下命令识别存储在表中的多边形的紧密顶点:vertices(polygon)
SELECT p1.id, p2.id, v1, v2
FROM polygons p1, polygons p2,
LATERAL vertices(p1.poly) v1,
LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;
也可以编写此查询
SELECT p1.id, p2.id, v1, v2
FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,
polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;
或其他几种等效配方。(如前所述,在此示例中不需要关键字,但为了清楚起见,我们使用它。LATERAL
对于子查询来说,它通常特别方便,因此即使子查询没有为源行生成任何行,源行也会出现在结果中。例如LEFT JOIN LATERAL,如果返回制造商生产的产品名称,但我们表中的一些制造商目前不生产任何产品,我们可以找出哪些产品是这样的:LATERAL get_product_names()
SELECT m.name
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true
WHERE pname IS NULL;
WHERE 子句的语法是
WHERE search_condition
完成子句的处理后,将根据搜索条件检查派生的虚拟表的每一行。如果条件的结果为 true,则该行将保留在输出表中,否则(即,如果结果为 false 或 null)则丢弃该行。搜索条件通常引用子句中生成的表的至少一列;这不是必需的,但否则该子句将毫无用处。
注意:内部连接的连接条件可以写在子句中,也可以写在子句中。例如,这些表表达式是等效的:WHERE JOIN FROM a, b WHERE a.id = b.id AND b.val > 5和:FROM a INNER JOIN b ON (a.id = b.id) WHERE b.val > 5 甚至可能:FROM a NATURAL JOIN b WHERE b.val > 5
您使用哪一个主要是风格问题。该子句中的语法可能不适用于其他 SQL 数据库管理系统,即使它位于 SQL 标准中也是如此。对于外部连接,别无选择:它们必须在子句中完成。外部联接的 or 子句不等同于条件,因为它会导致在最终结果中添加行(对于不匹配的输入行)以及删除行。
以下是一些子句示例:WHERE
SELECT ... FROM fdt WHERE c1 > 5
SELECT ... FROM fdt WHERE c1 IN (1, 2, 3)
SELECT ... FROM fdt WHERE c1 IN (SELECT c1 FROM t2)
SELECT ... FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10)
SELECT ... FROM fdt WHERE c1 BETWEEN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) AND 100
SELECT ... FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 > fdt.c1)
fdt是子句中派生的表。不满足子句搜索条件的行将从 中消除。请注意使用标量子查询作为值表达式。就像任何其他查询一样,子查询可以使用复杂的表表达式。另请注意如何在子查询中引用。仅当也是子查询的派生输入表中的列的名称时,才需要限定为 as。但是,即使不需要,限定列名称也会增加清晰度。此示例演示外部查询的列命名范围如何扩展到其内部查询。
通过筛选器后,派生的输入表可能会使用子句进行分组,并使用子句消除组行。WHEREGROUP BYHAVING
SELECT select_list
FROM ...
[WHERE ...]
GROUP BY grouping_column_reference [, grouping_column_reference]...
GROUP BY 子句用于将表中列出的所有列中具有相同值的行组合在一起。列的列出顺序无关紧要。其效果是将具有共同值的每组行合并到一个组行中,该组行表示组中的所有行。这样做是为了消除应用于这些组的输出和/或计算聚合中的冗余。例如:
=> SELECT * FROM test1;
x | y
---+---
a | 3
c | 2
b | 5
a | 1
(4 rows)
=> SELECT x FROM test1 GROUP BY x;
x
---
a
b
c
(3 rows)
在第二个查询中,我们不可能编写 ,因为该列没有可以与每个组关联的单个值。分组依据列可以在选择列表中引用,因为它们在每个组中都有一个值。SELECT * FROM test1 GROUP BY xy
通常,如果对表进行分组,则不能引用未列出的列,除非在聚合表达式中。聚合表达式的一个示例是:GROUP BY
=> SELECT x, sum(y) FROM test1 GROUP BY x;
x | sum
---+-----
a | 4
b | 5
c | 2
(3 rows)
下面是一个聚合函数,用于计算整个组的单个值。
提示:不使用聚合表达式的分组可以有效地计算列中的非重复值集。
下面是另一个示例:它计算每个产品的总销售额(而不是所有产品的总销售额):
SELECT product_id, p.name, (sum(s.units) * p.price) AS sales
FROM products p LEFT JOIN sales s USING (product_id)
GROUP BY product_id, p.name, p.price;
在此示例中,列 product_idp、namep 和price 必须位于子句中,因为它们在查询选择列表中被引用(但见下文)。该列不必在列表中,因为它仅在聚合表达式 () 中使用,该表达式表示产品的销售额。对于每个产品,查询将返回有关该产品所有销售额的摘要行。GROUP BYs.units GROUP BYsum(…)
如果将 products 表设置为主键,则在上面的示例中按分组就足够了,因为名称和价格在功能上取决于产品 ID,因此对于每个产品 ID 组返回哪个名称和价格值不会有歧义。
在严格的 SQL 中,只能按源表的列进行分组,但 PostgreSQL 将其扩展为也允许按选择列表中的列进行分组。还允许按值表达式而不是简单的列名进行分组。
如果已使用GROUP BY HAVING对表进行分组,但只对某些组感兴趣,则可以使用该子句(与子句非常相似)从结果中消除组。语法为:
SELECT select_list FROM ... [WHERE ...] GROUP BY ... HAVING boolean_expression
子句中的表达式既可以引用分组表达式,也可以引用未分组的表达式(必然涉及聚合函数)。HAVING
例:
=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;
x | sum
---+-----
a | 4
b | 5
(2 rows)
=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';
x | sum
---+-----
a | 4
b | 5
(2 rows)
同样,一个更现实的例子:
SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit
FROM products p LEFT JOIN sales s USING (product_id)
WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks'
GROUP BY product_id, p.name, p.price, p.cost
HAVING sum(p.price * s.units) > 5000;
在上面的示例中,该子句按未分组的列选择行(该表达式仅适用于过去 4 周的销售额),而该子句将输出限制为总销售额超过 5000 的组。请注意,聚合表达式不一定需要在查询的所有部分都相同。
如果查询包含聚合函数调用,但没有子句,则仍会进行分组:结果是单个组行(或者如果该行随后被 消除,则可能根本没有行)。如果它包含子句,即使没有任何聚合函数调用或子句,也是如此。
使用分组集的概念可以进行比上述操作更复杂的分组操作。和子句选择的数据按每个指定的分组集分别分组,聚合为每个组计算,就像计算简单子句一样,然后返回结果。例如:FROM WHERE GROUP BY
=> SELECT * FROM items_sold;
brand | size | sales
-------+------+-------
Foo | L | 10
Foo | M | 20
Bar | M | 15
Bar | L | 5
(4 rows)
=> SELECT brand, size, sum(sales) FROM items_sold GROUP BY GROUPING SETS ((brand), (size), ());
brand | size | sum
-------+------+-----
Foo | | 30
Bar | | 20
| L | 15
| M | 35
| | 50
(5 rows)
每个子列表可以指定零个或多个列或表达式,其解释方式与直接在子句中的解释方式相同。空分组集意味着所有行都聚合到一个组(即使不存在输入行也会输出),如上所述,对于没有子句的聚合函数。GROUPING SETSGROUP BY GROUP BY
对分组列或表达式的引用将替换为结果行中的 null 值,这些值不会出现在这些列中。
提供了用于指定两种常见类型的分组集的速记表示法。表单的子句
ROLLUP ( e1, e2, e3, ... )
表示给定的表达式列表和列表的所有前缀,包括空列表;因此,它等价于
GROUPING SETS (
( e1, e2, e3, ... ),
...
( e1, e2 ),
( e1 ),
( )
)
这通常用于对分层数据进行分析;例如,按部门、部门和全公司总工资划分的总工资。
CUBE ( e1, e2, ... )
表示给定列表及其所有可能的子集(即幂集)。因此
CUBE ( a, b, c )
相当于
ROUPING SETS (
( a, b, c ),
( a, b ),
( a, c ),
( a ),
( b, c ),
( b ),
( c ),
( )
)
或子句的各个元素可以是单个表达式,也可以是括号中元素的子列表。在后一种情况下,子列表被视为单个单元,以便生成单个分组集。例如:CUBE ROLLUP
CUBE ( (a, b), (c, d) )
相当于
GROUPING SETS (
( a, b, c, d ),
( a, b ),
( c, d ),
( )
)
和
ROLLUP ( a, (b, c), d )
相当于
GROUPING SETS (
( a, b, c, d ),
( a, b, c ),
( a ),
( )
)
and 构造可以直接在子句中使用,也可以嵌套在子句中。如果一个子句嵌套在另一个子句中,则效果与内部子句的所有元素都直接写在外部子句中相同。CUBEROLLUPGROUP BYGROUPING SETSGROUPING SETS
如果在单个子句中指定了多个分组项,则分组集的最终列表是各个项的叉积。例如:GROUP BY
GROUP BY a, CUBE (b, c), GROUPING SETS ((d), (e))
相当于
GROUP BY GROUPING SETS (
(a, b, c, d), (a, b, c, e),
(a, b, d), (a, b, e),
(a, c, d), (a, c, e),
(a, d), (a, e)
)
同时指定多个分组项时,最后一组分组集可能包含重复项。例如:
GROUP BY ROLLUP (a, b), ROLLUP (a, c)
相当于
GROUP BY GROUPING SETS (
(a, b, c),
(a, b),
(a, b),
(a, c),
(a),
(a),
(a, c),
(a),
()
)
如果这些重复项是不需要的,则可以直接使用 .因此:DISTINCTGROUP BY
GROUP BY DISTINCT ROLLUP (a, b), ROLLUP (a, c)
相当于
GROUP BY GROUPING SETS (
(a, b, c),
(a, b),
(a, c),
(a),
()
)
这与 using 不同,因为输出行可能仍包含重复项。如果任何未分组的列包含 NULL,则它与对同一列进行分组时使用的 NULL 无法区分。SELECT DISTINCT
注意:构造通常在表达式中被识别为行构造函数。在子句中,这不适用于表达式的顶层,并被解析为表达式列表,如上所述。如果由于某种原因需要在分组表达式中使用行构造函数,请使用
.(a, b) GROUP BY(a, b) ROW(a, b)