Beam ZetaSQL 查询语法
查询语句扫描一个或多个表、流或表达式并返回计算的结果行。
SQL 语法
query_statement: query_expr query_expr: [ WITH with_query_name AS ( query_expr ) [, ...] ] { select | ( query_expr ) | query_expr set_op query_expr } [ [ ORDER BY expression [{ ASC | DESC }] [, ...] ] LIMIT count [ OFFSET skip_rows ] ] select: SELECT [ ALL | DISTINCT ] { * | expression [ [ AS ] alias ] } [, ...] [ FROM from_item ] [ WHERE bool_expression ] [ GROUP BY expression [, ...] ] [ HAVING bool_expression ] set_op: UNION { ALL | DISTINCT } | INTERSECT { ALL | DISTINCT } | EXCEPT { ALL | DISTINCT } from_item: { table_name [ [ AS ] alias ] | join | ( query_expr ) [ [ AS ] alias ] | with_query_name [ [ AS ] alias ] } table_name: identifier [ . identifier ...] join: from_item [ join_type ] JOIN from_item ON bool_expression join_type: { INNER | FULL [OUTER] | LEFT [OUTER] | RIGHT [OUTER] }
符号
- 方括号“[ ]”表示可选子句。
- 圆括号“( )”表示字面圆括号。
- 竖线“|”表示逻辑 OR。
- 花括号“{ }”包含一组选项。
- 方括号“[, ... ]”中逗号后跟省略号表示前一项可以在逗号分隔列表中重复。
SELECT 列表
语法
SELECT [ ALL ]
{ * | expression [ [ AS ] alias ] } [, ...]
SELECT 列表定义查询将返回的列。SELECT 列表中的表达式可以引用其对应 FROM 子句中任何 from_item 中的列。
SELECT 列表中的每个项目都是以下之一
- *
表达式
SELECT *
SELECT *,通常称为选择星号,为执行完整查询后可见的每一列生成一个输出列。
SELECT * FROM (SELECT "apple" AS fruit, "carrot" AS vegetable);
+-------+-----------+
| fruit | vegetable |
+-------+-----------+
| apple | carrot |
+-------+-----------+SELECT 表达式
注意:在顶级 SELECT 中,您必须使用显式选择的列名,或者如果您使用的是表达式,则必须使用显式别名。
SELECT 列表中的项目可以是表达式。这些表达式计算为单个值并生成一个输出列,以及一个可选的显式别名。
如果表达式没有显式别名,它将根据隐式别名规则(如果可能)接收隐式别名。否则,该列将是匿名的,您无法在查询中的其他地方使用名称引用它。
SELECT 修饰符
您可以修改从 SELECT 查询返回的结果,如下所示。
SELECT ALL
SELECT ALL 语句返回所有行,包括重复行。SELECT ALL 是 SELECT 的默认行为。
别名
有关 SELECT 列表别名的语法和可见性信息,请参见别名。
FROM 子句
FROM 子句指示要从中检索行的表或流,并指定如何将这些行连接在一起以生成单个流,以便在查询的其余部分进行处理。
语法
from_item: { table_name [ [ AS ] alias ] | join | ( query_expr ) [ [ AS ] alias ] | with_query_name [ [ AS ] alias ] }
table_name
可由 Beam SQL 查询的数据源的完全限定 SQL 名称,由使用标准 SQL 词法结构的点分隔标识符列表指定。您必须使用反引号将包含非字母、数字或下划线的字符的标识符括起来。
SELECT * FROM bigquery.table.`my-project`.baseball.roster; SELECT * FROM pubsub.topic.`my-project`.incoming_events;
join
请参见下面的JOIN 类型。
select
( select ) [ [ AS ] 别名 ] 是一个表子查询。
with_query_name
WITH 子句(请参见WITH 子句)中的查询名称充当临时表的名称,您可以在 FROM 子句中的任何地方引用它们。在下面的示例中,subQ1 和 subQ2 是 with_query_name。
示例
WITH subQ1 AS (SELECT * FROM Roster WHERE SchoolID = 52), subQ2 AS (SELECT SchoolID FROM subQ1) SELECT DISTINCT * FROM subQ2;
WITH 子句会隐藏任何具有相同名称的永久表,直到查询结束,除非您限定表名,例如
db.Roster.
子查询
子查询是在另一个语句内部出现的查询,并且写在一对括号内。它们也称为“子 SELECT”或“嵌套 SELECT”。完整的 SELECT 语法在子查询中有效。
子查询有两种类型
- 表达式子查询,您可以在表达式有效的任何地方使用它。表达式子查询返回单个值。
- 表子查询,您只能在
FROM子句中使用它。外部查询将子查询的结果视为表。
请注意,两种类型的子查询都必须用括号括起来。
示例
SELECT AVG ( PointsScored )
FROM
( SELECT PointsScored
FROM Stats
WHERE SchoolID = 77 )表子查询可以选择具有别名。
示例
SELECT r.LastName
FROM
( SELECT * FROM Roster) AS r;别名
有关 FROM 子句别名的语法和可见性信息,请参见别名。
JOIN 类型
语法
join: from_item [ join_type ] JOIN from_item ON bool_expression join_type: { INNER | FULL [OUTER] | LEFT [OUTER] | RIGHT [OUTER] }
JOIN 子句合并两个 from_item,以便 SELECT 子句可以将它们查询为一个源。join_type 和 ON 子句(“连接条件”)指定如何组合和丢弃来自两个 from_item 的行,以形成单个源。
所有 JOIN 子句都需要 join_type。
[INNER] JOIN
INNER JOIN 或简称为 JOIN,实际上计算两个 from_item 的笛卡尔积,并丢弃所有不满足连接条件的行。“实际上”意味着可以实现 INNER JOIN,而无需实际计算笛卡尔积。
FULL [OUTER] JOIN
FULL OUTER JOIN(或简称为 FULL JOIN)返回两个 from_item 中所有满足连接条件的行中的所有字段。
FULL 表示返回两个 from_item 中的所有行,即使它们不满足连接条件。
OUTER 表示如果来自一个 from_item 的给定行未连接到另一个 from_item 中的任何行,则该行将返回,其中来自另一个 from_item 的所有列都为 NULL。
LEFT [OUTER] JOIN
LEFT OUTER JOIN(或简称为 LEFT JOIN)针对两个 from_item 的结果始终保留 JOIN 子句中左侧 from_item 中的所有行,即使右侧 from_item 中没有行满足连接谓词。
LEFT 表示返回左侧 from_item 中的所有行;如果来自左侧 from_item 的给定行未连接到右侧 from_item 中的任何行,则该行将返回,其中来自右侧 from_item 的所有列都为 NULL。未连接到左侧 from_item 中任何行的右侧 from_item 中的行将被丢弃。
RIGHT [OUTER] JOIN
RIGHT OUTER JOIN(或简称为 RIGHT JOIN)的结果类似且与 LEFT OUTER JOIN 对称。
ON 子句
ON 子句包含一个 bool_expression。如果 bool_expression 返回 TRUE,则组合行(连接两行的结果)满足连接条件。
示例
SELECT * FROM Roster INNER JOIN PlayerStats
ON Roster.LastName = PlayerStats.LastName;JOIN 序列
FROM 子句可以按顺序包含多个 JOIN 子句。
示例
SELECT * FROM a LEFT JOIN b ON TRUE LEFT JOIN c ON TRUE;其中 a、b 和 c 是任何 from_item。JOIN 从左到右绑定,但您可以插入圆括号以将它们按不同的顺序分组。
WHERE 子句
语法
WHERE bool_expressionWHERE 子句通过针对 bool_expression 评估每一行来过滤行,并丢弃所有未返回 TRUE 的行(即返回 FALSE 或 NULL 的行)。
示例
SELECT * FROM Roster
WHERE SchoolID = 52;bool_expression 可以包含多个子条件。
示例
SELECT * FROM Roster
WHERE STARTS_WITH(LastName, "Mc") OR STARTS_WITH(LastName, "Mac");您不能在 WHERE 子句中引用来自 SELECT 列表的列别名。
GROUP BY 子句
语法
GROUP BY expression [, ...]
GROUP BY 子句将具有 GROUP BY 子句中 expression 的非唯一值的表中的行分组在一起。对于源表中具有 expression 的非唯一值的多个行,GROUP BY 子句将生成一个组合的单个行。GROUP BY 通常与 SELECT 列表中存在聚合函数时一起使用,或用于消除输出中的冗余。expression 的数据类型必须是可分组。
示例
SELECT SUM(PointsScored), LastName
FROM PlayerStats
GROUP BY LastName;GROUP BY 子句可以引用 SELECT 列表中的表达式名称。GROUP BY 子句还允许使用整数值对 SELECT 列表中的表达式进行序数引用。1 指的是 SELECT 列表中的第一个表达式,2 指的是第二个表达式,依此类推。表达式列表可以组合序数和表达式名称。
示例
SELECT SUM(PointsScored), LastName, FirstName
FROM PlayerStats
GROUP BY LastName, FirstName;上面的查询等效于
SELECT SUM(PointsScored), LastName, FirstName
FROM PlayerStats
GROUP BY 2, FirstName;GROUP BY 子句还可以引用别名。如果查询在 SELECT 子句中包含别名,则这些别名会覆盖对应 FROM 子句中的名称。
示例
SELECT SUM(PointsScored), LastName as last_name
FROM PlayerStats
GROUP BY last_name;HAVING 子句
语法
HAVING bool_expressionHAVING 子句类似于 WHERE 子句:它会过滤掉在针对 bool_expression 进行评估时未返回 TRUE 的行。
与 WHERE 子句一样,bool_expression 可以是返回布尔值的任何表达式,并且可以包含多个子条件。
HAVING 子句与 WHERE 子句的不同之处在于
HAVING子句要求查询中存在GROUP BY或聚合操作。HAVING子句出现在GROUP BY和聚合操作之后,ORDER BY之前。这意味着HAVING子句针对结果集中的每个聚合行进行评估。这与WHERE子句不同,WHERE子句在GROUP BY和聚合操作之前进行评估。
HAVING 子句可以引用通过 FROM 子句提供的列,以及 SELECT 列表别名。HAVING 子句中引用的表达式必须出现在 GROUP BY 子句中,或者它们必须是聚合函数的结果。
SELECT LastName
FROM Roster
GROUP BY LastName
HAVING SUM(PointsScored) > 15;如果查询在 SELECT 子句中包含别名,则这些别名将覆盖 FROM 子句中的名称。
SELECT LastName, SUM(PointsScored) AS ps
FROM Roster
GROUP BY LastName
HAVING ps > 0;强制聚合
聚合操作不必出现在 HAVING 子句本身中,但必须至少以以下形式之一出现:
SELECT 列表中的聚合函数。
SELECT LastName, SUM(PointsScored) AS total
FROM PlayerStats
GROUP BY LastName
HAVING total > 15;HAVING 子句中的聚合函数。
SELECT LastName
FROM PlayerStats
GROUP BY LastName
HAVING SUM(PointsScored) > 15;SELECT 列表和 HAVING 子句中都存在聚合操作。
当 SELECT 列表和 HAVING 子句中都存在聚合函数时,聚合函数及其引用的列不必相同。在下面的示例中,两个聚合函数 COUNT() 和 SUM() 不同,并且使用不同的列。
SELECT LastName, COUNT(*)
FROM PlayerStats
GROUP BY LastName
HAVING SUM(PointsScored) > 15;LIMIT 子句和 OFFSET 子句
语法
[ ORDER BY expression [{ASC | DESC}] [,...] ] LIMIT count [ OFFSET skip_rows ]ORDER BY 子句指定一个列或表达式作为结果集的排序标准。如果不存在 ORDER BY 子句,则查询结果的顺序未定义。默认排序方向为 ASC,它按 expression 值的升序对结果进行排序。DESC 按降序对结果进行排序。允许使用 FROM 子句或 SELECT 列表中的列别名。如果查询在 SELECT 子句中包含别名,则这些别名将覆盖相应 FROM 子句中的名称。
可以按多个列排序。
在对值进行排序时,适用以下规则:
- NULL 值:在
ORDER BY子句的上下文中,NULL 值是最小可能的值;也就是说,NULL 值在ASC排序中首先出现,在DESC排序中最后出现。
LIMIT 指定一个非负的 count,类型为 INT64,并且最多返回 count 行。LIMIT 0 返回 0 行。如果存在集合操作,则 LIMIT 在评估集合操作后应用。
OFFSET 指定一个非负的 skip_rows,类型为 INT64,并且只考虑从表中的该偏移量开始的行。
这些子句只接受字面量或参数值。
由 LIMIT 和 OFFSET 返回的行未指定,除非这些运算符在 ORDER BY 之后使用。
WITH 子句
WITH 子句将一个或多个命名子查询的结果绑定到临时表名。每个引入的表名在同一查询表达式中的后续 SELECT 表达式中可见。这包括以下类型的 SELECT 表达式:
- 后续
WITH绑定中的任何SELECT表达式。 - 集合运算符(如
UNION)两侧查询表达式中的顶层SELECT表达式。 - 同一查询表达式中的子查询内部的
SELECT表达式。
示例
WITH subQ1 AS (SELECT SchoolID FROM Roster),
subQ2 AS (SELECT OpponentID FROM PlayerStats)
SELECT * FROM subQ1
UNION ALL
SELECT * FROM subQ2;以下是 WITH 子句的范围规则:
- 别名的范围为,在
WITH子句中引入的别名仅在同一WITH子句中的后续子查询以及WITH子句下的查询中可见。 - 在同一个
WITH子句中引入的别名必须是唯一的,但同一个别名可以在同一个查询中的多个WITH子句中使用。局部别名会覆盖在局部别名可见的任何地方的任何外部别名。
Beam SQL 不支持 WITH RECURSIVE。
别名
别名是在查询中出现的表、列或表达式的临时名称。可以在 SELECT 列表或 FROM 子句中引入显式别名。
显式别名语法
可以在 FROM 子句或 SELECT 列表中引入显式别名。
在 FROM 子句中,可以使用 [AS] alias 为任何项目(包括表、数组和子查询)引入显式别名。AS 关键字是可选的。
示例
SELECT s.FirstName, s2.SongName
FROM Singers AS s, (SELECT * FROM Songs) AS s2;可以使用 [AS] alias 为 SELECT 列表中的任何表达式引入显式别名。AS 关键字是可选的。
示例
SELECT s.FirstName AS name, LOWER(s.FirstName) AS lname
FROM Singers s;显式别名可见性
在查询中引入显式别名后,对在查询中的其他位置引用该别名的位置存在限制。这些别名可见性的限制是 Beam SQL 的名称作用域规则的结果。
FROM 子句别名
Beam SQL 从左到右处理 FROM 子句中的别名,并且别名仅对 FROM 子句中后续的路径表达式可见。
示例
假设 Singers 表有一个类型为 ARRAY 的 Concerts 列。
SELECT FirstName
FROM Singers AS s, s.Concerts;无效
SELECT FirstName
FROM s.Concerts, Singers AS s; // INVALID.FROM 子句别名不可见于同一个 FROM 子句中的子查询。FROM 子句中的子查询不能包含对同一个 FROM 子句中的其他表的相关引用。
无效
SELECT FirstName
FROM Singers AS s, (SELECT (2020 - ReleaseDate) FROM s) // INVALID.可以在查询中的任何位置使用 FROM 中表的任何列名作为别名,无论是否使用表名进行限定。
示例
SELECT FirstName, s.ReleaseDate
FROM Singers s WHERE ReleaseDate = 1975;SELECT 列表别名
SELECT 列表中的别名仅对以下子句可见:
GROUP BY子句ORDER BY子句HAVING子句
示例
SELECT LastName AS last, SingerID
FROM Singers
ORDER BY last;GROUP BY、ORDER BY 和 HAVING 子句中的显式别名
这三个子句(GROUP BY、ORDER BY 和 HAVING)只能引用以下值:
FROM子句中的表及其任何列。SELECT列表中的别名。
GROUP BY 和 ORDER BY 还可以引用第三组值:
- 整数字面量,它引用
SELECT列表中的项目。整数1指的是SELECT列表中的第一个项目,2指的是第二个项目,等等。
示例
SELECT SingerID AS sid, COUNT(Songid) AS s2id
FROM Songs
GROUP BY 1
ORDER BY 2 DESC LIMIT 10;上面的查询等效于
SELECT SingerID AS sid, COUNT(Songid) AS s2id
FROM Songs
GROUP BY sid
ORDER BY s2id DESC LIMIT 10;不明确的别名
如果名称不明确,意味着它可以解析为多个唯一对象,则 Beam SQL 会提供错误。
示例
此查询包含表之间冲突的列名,因为 Singers 和 Songs 都有一个名为 SingerID 的列。
SELECT SingerID
FROM Singers, Songs;此查询包含在 GROUP BY 子句中不明确的别名,因为它们在 SELECT 列表中重复。
SELECT FirstName AS name, LastName AS name,
FROM Singers
GROUP BY name;FROM 子句列名与 SELECT 列表别名在 GROUP BY 中的歧义
SELECT UPPER(LastName) AS LastName
FROM Singers
GROUP BY LastName;上面的查询不明确,并且会产生错误,因为 GROUP BY 子句中的 LastName 可能指的是 Singers 中的原始列 LastName,也可能指的是别名 AS LastName,其值为 UPPER(LastName)。
相同的不确定性规则适用于路径表达式。考虑以下查询,其中 table 有列 x 和 y,并且列 z 类型为 STRUCT 并且具有字段 v、w 和 x。
示例
SELECT x, z AS T
FROM table T
GROUP BY T.x;别名 T 是不明确的,并且会产生错误,因为 GROUP BY 子句中的 T.x 可能指的是 table.x 或 table.z.x。
如果一个名称既是列名又是 SELECT 列表别名,只要名称解析为同一个底层对象,则该名称在 GROUP BY、ORDER BY 或 HAVING 中不会是模棱两可的。
示例
SELECT LastName, BirthYear AS BirthYear
FROM Singers
GROUP BY BirthYear;别名 BirthYear 不是模棱两可的,因为它解析为同一个底层列 Singers.BirthYear。
附录 A:包含示例数据的示例
示例表
以下三个表包含关于运动员、他们的学校以及他们在赛季中获得的分数的示例数据。这些表将用于说明不同查询子句的行为。
表 Roster
| LastName | SchoolID |
|---|---|
| Adams | 50 |
| Buchanan | 52 |
| Coolidge | 52 |
| Davis | 51 |
| Eisenhower | 77 |
Roster 表包含球员姓名(LastName)和分配给其学校的唯一 ID(SchoolID)的列表。
表 PlayerStats
| LastName | OpponentID | PointsScored |
|---|---|---|
| Adams | 51 | 3 |
| Buchanan | 77 | 0 |
| Coolidge | 77 | 1 |
| Adams | 52 | 4 |
| Buchanan | 50 | 13 |
PlayerStats 表包含球员姓名(LastName)和分配给他们在给定比赛中遇到的对手的唯一 ID(OpponentID)以及运动员在该场比赛中获得的得分(PointsScored)的列表。
表 TeamMascot
| SchoolId | Mascot |
|---|---|
| 50 | Jaguars |
| 51 | Knights |
| 52 | Lakers |
| 53 | Mustangs |
TeamMascot 表包含学校唯一 ID(SchoolID)和该学校的吉祥物(Mascot)的列表。
JOIN 类型
1) [INNER] JOIN
示例
SELECT * FROM Roster JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;结果
| LastName | Roster.SchoolId | TeamMascot.SchoolId | Mascot |
|---|---|---|---|
| Adams | 50 | 50 | Jaguars |
| Buchanan | 52 | 52 | Lakers |
| Coolidge | 52 | 52 | Lakers |
| Davis | 51 | 51 | Knights |
2) FULL [OUTER] JOIN
示例
SELECT * FROM Roster FULL JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;| LastName | Roster.SchoolId | TeamMascot.SchoolId | Mascot |
|---|---|---|---|
| Adams | 50 | 50 | Jaguars |
| Buchanan | 52 | 52 | Lakers |
| Coolidge | 52 | 52 | Lakers |
| Davis | 51 | 51 | Knights |
| Eisenhower | 77 | NULL | NULL |
| NULL | NULL | 53 | Mustangs |
3) LEFT [OUTER] JOIN
示例
SELECT * FROM Roster LEFT JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;结果
| LastName | Roster.SchoolId | TeamMascot.SchoolId | Mascot |
|---|---|---|---|
| Adams | 50 | 50 | Jaguars |
| Buchanan | 52 | 52 | Lakers |
| Coolidge | 52 | 52 | Lakers |
| Davis | 51 | 51 | Knights |
| Eisenhower | 77 | NULL | NULL |
4) RIGHT [OUTER] JOIN
示例
SELECT * FROM Roster RIGHT JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;结果
| LastName | Roster.SchoolId | TeamMascot.SchoolId | Mascot |
|---|---|---|---|
| Adams | 50 | 50 | Jaguars |
| Davis | 51 | 51 | Knights |
| Coolidge | 52 | 52 | Lakers |
| Buchanan | 52 | 52 | Lakers |
| NULL | NULL | 53 | Mustangs |
GROUP BY 子句
示例
SELECT LastName, SUM(PointsScored)
FROM PlayerStats
GROUP BY LastName;| LastName | SUM |
|---|---|
| Adams | 7 |
| Buchanan | 13 |
| Coolidge | 1 |
集合运算符
UNION
UNION 运算符通过将来自每个 SELECT 语句结果集的列配对并将它们垂直连接起来,来组合两个或多个 SELECT 语句的结果集。
示例
SELECT Mascot AS X, SchoolID AS Y
FROM TeamMascot
UNION ALL
SELECT LastName, PointsScored
FROM PlayerStats;结果
| X | Y |
|---|---|
| Jaguars | 50 |
| Knights | 51 |
| Lakers | 52 |
| Mustangs | 53 |
| Adams | 3 |
| Buchanan | 0 |
| Coolidge | 1 |
| Adams | 4 |
| Buchanan | 13 |
INTERSECT
此查询返回 Roster 和 PlayerStats 中都存在的姓氏。
SELECT LastName
FROM Roster
INTERSECT ALL
SELECT LastName
FROM PlayerStats;结果
| LastName |
|---|
| Adams |
| Coolidge |
| Buchanan |
EXCEPT
下面的查询返回 Roster 中不存在于 PlayerStats 中的姓氏。
SELECT LastName
FROM Roster
EXCEPT DISTINCT
SELECT LastName
FROM PlayerStats;结果
| LastName |
|---|
| Eisenhower |
| Davis |
反转 SELECT 语句的顺序将返回 PlayerStats 中不存在于 Roster 中的姓氏。
SELECT LastName
FROM PlayerStats
EXCEPT DISTINCT
SELECT LastName
FROM Roster;结果
(empty)

