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] }

符号

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 ALLSELECT 的默认行为。

别名

有关 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 子句中的任何地方引用它们。在下面的示例中,subQ1subQ2with_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 语法在子查询中有效。

子查询有两种类型

请注意,两种类型的子查询都必须用括号括起来。

示例

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_typeON 子句(“连接条件”)指定如何组合和丢弃来自两个 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;

其中 abc 是任何 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 子句可以引用通过 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 子句中的名称。

可以按多个列排序。

在对值进行排序时,适用以下规则:

LIMIT 指定一个非负的 count,类型为 INT64,并且最多返回 count 行。LIMIT 0 返回 0 行。如果存在集合操作,则 LIMIT 在评估集合操作后应用。

OFFSET 指定一个非负的 skip_rows,类型为 INT64,并且只考虑从表中的该偏移量开始的行。

这些子句只接受字面量或参数值。

LIMITOFFSET 返回的行未指定,除非这些运算符在 ORDER BY 之后使用。

WITH 子句

WITH 子句将一个或多个命名子查询的结果绑定到临时表名。每个引入的表名在同一查询表达式中的后续 SELECT 表达式中可见。这包括以下类型的 SELECT 表达式:

示例

WITH subQ1 AS (SELECT SchoolID FROM Roster),
     subQ2 AS (SELECT OpponentID FROM PlayerStats)
SELECT * FROM subQ1
UNION ALL
SELECT * FROM subQ2;

以下是 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] aliasSELECT 列表中的任何表达式引入显式别名。AS 关键字是可选的。

示例

SELECT s.FirstName AS name, LOWER(s.FirstName) AS lname
FROM Singers s;

显式别名可见性

在查询中引入显式别名后,对在查询中的其他位置引用该别名的位置存在限制。这些别名可见性的限制是 Beam SQL 的名称作用域规则的结果。

FROM 子句别名

Beam SQL 从左到右处理 FROM 子句中的别名,并且别名仅对 FROM 子句中后续的路径表达式可见。

示例

假设 Singers 表有一个类型为 ARRAYConcerts 列。

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 列表中的别名对以下子句可见:

示例

SELECT LastName AS last, SingerID
FROM Singers
ORDER BY last;

GROUP BY、ORDER BY 和 HAVING 子句中的显式别名

这三个子句(GROUP BYORDER BYHAVING)只能引用以下值:

GROUP BYORDER BY 还可以引用第三组值:

示例

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 会提供错误。

示例

此查询包含表之间冲突的列名,因为 SingersSongs 都有一个名为 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 有列 xy,并且列 z 类型为 STRUCT 并且具有字段 vwx

示例

SELECT x, z AS T
FROM table T
GROUP BY T.x;

别名 T 是不明确的,并且会产生错误,因为 GROUP BY 子句中的 T.x 可能指的是 table.xtable.z.x

如果一个名称既是列名又是 SELECT 列表别名,只要名称解析为同一个底层对象,则该名称在 GROUP BYORDER BYHAVING不会是模棱两可的。

示例

SELECT LastName, BirthYear AS BirthYear
FROM Singers
GROUP BY BirthYear;

别名 BirthYear 不是模棱两可的,因为它解析为同一个底层列 Singers.BirthYear

附录 A:包含示例数据的示例

示例表

以下三个表包含关于运动员、他们的学校以及他们在赛季中获得的分数的示例数据。这些表将用于说明不同查询子句的行为。

表 Roster

LastNameSchoolID
Adams50
Buchanan52
Coolidge52
Davis51
Eisenhower77

Roster 表包含球员姓名(LastName)和分配给其学校的唯一 ID(SchoolID)的列表。

表 PlayerStats

LastNameOpponentIDPointsScored
Adams513
Buchanan770
Coolidge771
Adams524
Buchanan5013

PlayerStats 表包含球员姓名(LastName)和分配给他们在给定比赛中遇到的对手的唯一 ID(OpponentID)以及运动员在该场比赛中获得的得分(PointsScored)的列表。

表 TeamMascot

SchoolIdMascot
50Jaguars
51Knights
52Lakers
53Mustangs

TeamMascot 表包含学校唯一 ID(SchoolID)和该学校的吉祥物(Mascot)的列表。

JOIN 类型

1) [INNER] JOIN

示例

SELECT * FROM Roster JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;

结果

LastNameRoster.SchoolIdTeamMascot.SchoolIdMascot
Adams5050Jaguars
Buchanan5252Lakers
Coolidge5252Lakers
Davis5151Knights

2) FULL [OUTER] JOIN

示例

SELECT * FROM Roster FULL JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;
LastNameRoster.SchoolIdTeamMascot.SchoolIdMascot
Adams5050Jaguars
Buchanan5252Lakers
Coolidge5252Lakers
Davis5151Knights
Eisenhower77NULLNULL
NULLNULL53Mustangs

3) LEFT [OUTER] JOIN

示例

SELECT * FROM Roster LEFT JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;

结果

LastNameRoster.SchoolIdTeamMascot.SchoolIdMascot
Adams5050Jaguars
Buchanan5252Lakers
Coolidge5252Lakers
Davis5151Knights
Eisenhower77NULLNULL

4) RIGHT [OUTER] JOIN

示例

SELECT * FROM Roster RIGHT JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;

结果

LastNameRoster.SchoolIdTeamMascot.SchoolIdMascot
Adams5050Jaguars
Davis5151Knights
Coolidge5252Lakers
Buchanan5252Lakers
NULLNULL53Mustangs

GROUP BY 子句

示例

SELECT LastName, SUM(PointsScored)
FROM PlayerStats
GROUP BY LastName;
LastNameSUM
Adams7
Buchanan13
Coolidge1

集合运算符

UNION

UNION 运算符通过将来自每个 SELECT 语句结果集的列配对并将它们垂直连接起来,来组合两个或多个 SELECT 语句的结果集。

示例

SELECT Mascot AS X, SchoolID AS Y
FROM TeamMascot
UNION ALL
SELECT LastName, PointsScored
FROM PlayerStats;

结果

XY
Jaguars50
Knights51
Lakers52
Mustangs53
Adams3
Buchanan0
Coolidge1
Adams4
Buchanan13

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)