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_expression
WHERE
子句通过针对 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_expression
HAVING
子句类似于 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)