Beam Calcite SQL 查詢語法
查詢陳述式會掃描一個或多個資料表或表達式,並傳回計算出的結果列。
一般來說,查詢的語意是標準的。請參閱以下章節以瞭解支援 Beam 統一批次/串流模型的擴充功能
Beam SQL 的主要功能是 SELECT
陳述式。這是您查詢和聯結資料的方式。支援的操作是 Apache Calcite SQL 的子集。
SQL 語法
query_statement:
[ WITH with_query_name AS ( query_expr ) [, ...] ]
query_expr
query_expr:
{ select | ( query_expr ) | query_expr set_op query_expr }
[ LIMIT count [ OFFSET skip_rows ] ]
select:
SELECT [{ ALL | DISTINCT }]
{ [ expression. ]* [ EXCEPT ( column_name [, ...] ) ]
[ REPLACE ( expression [ AS ] column_name [, ...] ) ]
| expression [ [ AS ] alias ] } [, ...]
[ FROM from_item [, ...] ]
[ WHERE bool_expression ]
[ GROUP BY { expression [, ...] } ]
[ HAVING bool_expression ]
set_op:
UNION { ALL | DISTINCT } | INTERSECT DISTINCT | EXCEPT DISTINCT
from_item: {
table_name [ [ AS ] alias ] |
join |
( query_expr ) [ [ AS ] alias ]
with_query_name [ [ AS ] alias ]
}
join:
from_item [ join_type ] JOIN from_item
[ { ON bool_expression | USING ( join_column [, ...] ) } ]
join_type:
{ INNER | CROSS | FULL [OUTER] | LEFT [OUTER] | RIGHT [OUTER] }
符號表示法
- 方括號「[ ]」表示可選的子句。
- 圓括號「( )」表示字面圓括號。
- 垂直線「|」表示邏輯 OR。
- 大括號「{ }」括住一組選項。
- 方括號內的逗號後接省略號「[, … ]」表示前面的項目可以在以逗號分隔的清單中重複。
SELECT 清單
語法
SELECT [{ ALL | DISTINCT }]
{ [ expression. ]*
| expression [ [ AS ] alias ] } [, ...]
SELECT
清單定義查詢將傳回的資料欄。SELECT
清單中的表達式可以參考其對應 FROM
子句中任何 from_item
的資料欄。
SELECT
清單中的每個項目都是下列其中一種
- *
expression
expression.*
SELECT *
SELECT *
,通常稱為選取星號,會為執行完整查詢後可見的每個資料欄產生一個輸出資料欄。
SELECT * FROM (SELECT 'apple' AS fruit, 'carrot' AS vegetable);
+-------+-----------+
| fruit | vegetable |
+-------+-----------+
| apple | carrot |
+-------+-----------+
SELECT expression
SELECT
清單中的項目可以是表達式。這些表達式會評估為單一值,並產生一個輸出資料欄,並具有可選的明確 alias
。
如果表達式沒有明確的別名,則會根據隱含別名的規則接收隱含的別名(如果可能)。否則,該資料欄是匿名的,您無法在查詢中的其他地方按名稱引用它。
SELECT expression.*
SELECT
清單中的項目也可以採用 expression.*
的形式。這會為 expression
的每個資料欄或最上層欄位產生一個輸出資料欄。該表達式必須是資料表別名。
以下查詢會為資料表 groceries
中每個資料欄產生一個輸出資料欄,別名為 g
。
WITH groceries AS
(SELECT 'milk' AS dairy,
'eggs' AS protein,
'bread' AS grain)
SELECT g.*
FROM groceries AS g;
+-------+---------+-------+
| dairy | protein | grain |
+-------+---------+-------+
| milk | eggs | bread |
+-------+---------+-------+
SELECT 修飾符
您可以修改 SELECT
查詢傳回的結果,如下所示。
SELECT DISTINCT
SELECT DISTINCT
陳述式會捨棄重複的列,只傳回剩餘的列。SELECT DISTINCT
無法傳回下列類型的資料欄
- STRUCT
- ARRAY
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
現有資料表(可選地限定)的名稱。
SELECT * FROM Roster;
SELECT * FROM beam.Roster;
join
select
( select ) [ [ AS ] alias ]
是一個資料表子查詢。
with_query_name
WITH
子句中的查詢名稱(請參閱WITH 子句)的作用就像暫時資料表的名稱,您可以在 FROM
子句中的任何位置參考它們。在下面的範例中,subQ1
和 subQ2
是 with_query_names
。
範例
WITH
subQ1 AS (SELECT * FROM Roster WHERE SchoolID = 52),
subQ2 AS (SELECT SchoolID FROM subQ1)
SELECT DISTINCT * FROM subQ2;
WITH
子句會隱藏任何同名的永久資料表,除非您限定資料表名稱,例如 beam.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 | USING ( join_column [, ...] ) ]
join_type:
{ INNER | CROSS | FULL [OUTER] | LEFT [OUTER] | RIGHT [OUTER] }
JOIN
子句會合併兩個 from_item
,以便 SELECT
子句可以將它們作為一個來源查詢。join_type
和 ON
或 USING
子句(「聯結條件」)指定如何合併和捨棄兩個 from_item
中的列,以形成單一來源。
所有 JOIN
子句都需要 join_type
。
JOIN
子句需要聯結條件,除非下列其中一個條件成立
join_type
為CROSS
。- 其中一個或兩個
from_item
都不是資料表,例如array_path
或field_path
。
[INNER] JOIN
INNER JOIN
,或簡稱 JOIN
,會有效地計算兩個 from_item
的笛卡爾積,並捨棄所有不符合聯結條件的列。「有效地」表示可以實作 INNER JOIN
而無需實際計算笛卡爾積。
CROSS JOIN
一般來說,尚不支援 CROSS 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
對於兩個 from_item
執行 LEFT OUTER JOIN
(或簡稱 LEFT 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;
USING 子句
USING
子句需要一個或多個欄位的 column_list
,這些欄位會同時出現在兩個輸入表格中。它會對該欄位執行相等比較,如果相等比較傳回 TRUE,則資料列符合聯結條件。
在大多數情況下,使用 USING
關鍵字的語句等同於使用 ON
關鍵字的語句。例如,以下語句
SELECT FirstName
FROM Roster INNER JOIN PlayerStats
USING (LastName);
等同於
SELECT FirstName
FROM Roster INNER JOIN PlayerStats
ON Roster.LastName = PlayerStats.LastName;
當您使用 SELECT *
時,使用 USING
的查詢結果與使用 ON
的查詢結果會有所不同。為了說明這一點,請考慮以下查詢
SELECT * FROM Roster INNER JOIN PlayerStats
USING (LastName);
此語句會傳回 Roster
和 PlayerStats
的資料列,其中 Roster.LastName
與 PlayerStats.LastName
相同。結果會包含單個 LastName
欄位。
相反地,請考慮以下查詢
SELECT * FROM Roster INNER JOIN PlayerStats
ON Roster.LastName = PlayerStats.LastName;
此語句會傳回 Roster
和 PlayerStats
的資料列,其中 Roster.LastName
與 PlayerStats.LastName
相同。結果會包含兩個 LastName
欄位;一個來自 Roster
,另一個來自 PlayerStats
。
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 LastName LIKE 'Mc%' OR LastName LIKE 'Mac%';
您無法在 WHERE
子句中參照 SELECT
清單中的欄位別名。
在 INNER JOIN
中的運算式在 WHERE
子句中具有對等運算式。例如,使用 INNER
JOIN
和 ON
的查詢具有使用 CROSS JOIN
和 WHERE
的對等運算式。
範例 - 這個查詢
SELECT * FROM Roster INNER JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;
等同於
SELECT * FROM Roster CROSS JOIN TeamMascot
WHERE Roster.SchoolID = TeamMascot.SchoolID;
GROUP BY 子句
另請參閱視窗化與觸發
語法
GROUP BY { expression [, ...] }
GROUP BY
子句會將表格中 GROUP BY
子句中 expression
值非相異的資料列分組在一起。對於來源表格中 expression
值非相異的多個資料列,GROUP BY
子句會產生單個組合的資料列。當 SELECT
清單中存在彙總函式時,或為了消除輸出中的冗餘時,通常會使用 GROUP BY
。
範例
SELECT SUM(PointsScored), LastName
FROM PlayerStats
GROUP BY LastName;
HAVING 子句
語法
HAVING bool_expression
HAVING
子句與 WHERE
子句類似:它會篩選出針對 bool_expression
評估時未傳回 TRUE 的資料列。
與 WHERE
子句一樣,bool_expression
可以是任何傳回布林值的運算式,並且可以包含多個子條件。
HAVING
子句與 WHERE
子句的不同之處在於
HAVING
子句要求查詢中必須存在GROUP BY
或彙總。HAVING
子句出現在GROUP BY
和彙總之後。這表示會針對結果集中每個彙總的資料列評估一次HAVING
子句。這與WHERE
子句不同,WHERE
子句是在GROUP BY
和彙總之前評估。
HAVING
子句可以參照透過 FROM
子句提供的欄位,以及 SELECT
清單別名。HAVING
子句中參照的運算式必須出現在 GROUP BY
子句中,否則它們必須是彙總函式的結果
SELECT LastName
FROM Roster
GROUP BY LastName
HAVING SUM(PointsScored) > 15;
集合運算子
語法
UNION { ALL | DISTINCT } | INTERSECT DISTINCT | EXCEPT DISTINCT
集合運算子會將兩個或多個輸入查詢的結果組合到單個結果集中。您必須指定 ALL
或 DISTINCT
;如果您指定 ALL
,則會保留所有資料列。如果指定 DISTINCT
,則會捨棄重複的資料列。
如果給定的資料列 R 在第一個輸入查詢中恰好出現 m 次,而在第二個輸入查詢中出現 n 次 (m >= 0, n >= 0)
- 對於
UNION ALL
,R 在結果中恰好出現 m + n 次。 - 對於
UNION DISTINCT
,會在計算UNION
之後計算DISTINCT
,因此 R 恰好出現一次。 - 對於
INTERSECT DISTINCT
,會在計算上述結果之後計算DISTINCT
。 - 對於
EXCEPT DISTINCT
,如果 m > 0 且 n = 0,則資料列 R 會在輸出中出現一次。 - 如果有兩個以上的輸入查詢,則上述運算會被推廣,並且輸出與輸入從左到右以漸進方式組合時的輸出相同。
適用下列規則
- 對於
UNION ALL
以外的集合運算,所有欄位類型都必須支援相等比較。 - 運算子每一側的輸入查詢都必須傳回相同數量的欄位。
- 運算子會根據欄位在其各自
SELECT
清單中的位置,配對每個輸入查詢傳回的欄位。也就是說,第一個輸入查詢中的第一個欄位會與第二個輸入查詢中的第一個欄位配對。 - 結果集總是使用第一個輸入查詢中的欄位名稱。
- 結果集總是使用相應欄位中輸入類型的超類型,因此配對的欄位也必須具有相同的資料類型或共同的超類型。
- 您必須使用括號來分隔不同的集合運算;為此,
UNION ALL
和UNION DISTINCT
等集合運算有所不同。如果語句只重複相同的集合運算,則不需要括號。
範例
query1 UNION ALL (query2 UNION DISTINCT query3)
query1 UNION ALL query2 UNION ALL query3
無效
query1 UNION ALL query2 UNION DISTINCT query3
query1 UNION ALL query2 INTERSECT ALL query3; // INVALID.
UNION
UNION
運算子會透過配對每個查詢結果集中的欄位並垂直串連它們,來合併兩個或多個輸入查詢的結果集。
INTERSECT
INTERSECT
運算子會傳回在左側和右側輸入查詢的結果集中都找到的資料列。與 EXCEPT
不同,輸入查詢的位置(在 INTERSECT
運算子的左側或右側)並不重要。
EXCEPT
EXCEPT
運算子會傳回左側輸入查詢中存在,但右側輸入查詢中不存在的資料列。
LIMIT 子句和 OFFSET 子句
語法
LIMIT count [ OFFSET skip_rows ]
LIMIT
指定一個非負的 count
,類型為 INTEGER,且不會傳回超過 count
個資料列。LIMIT
0
會傳回 0 個資料列。如果存在集合運算,則會在評估集合運算後套用 LIMIT
。
OFFSET
指定一個非負的 skip_rows
,類型為 INTEGER,並且只會考慮表格中該偏移量後的資料列。
這些子句只接受常值或參數值。
LIMIT
和 OFFSET
傳回的資料列未指定。
WITH 子句
WITH
子句包含一個或多個已命名的子查詢,這些子查詢會在後續的 SELECT
語句每次參照它們時執行。任何子句或子查詢都可以參照您在 WITH
子句中定義的子查詢。這包括集合運算子(例如 UNION
)任一側的任何 SELECT
語句。
範例
WITH subQ1 AS (SELECT SchoolID FROM Roster),
subQ2 AS (SELECT OpponentID FROM PlayerStats)
SELECT * FROM subQ1
UNION ALL
SELECT * FROM subQ2;
別名
別名是賦予查詢中表格、欄位或運算式的暫時性名稱。您可以在 SELECT
清單或 FROM
子句中引入明確別名,或者 Beam 會為某些運算式推斷隱含別名。既沒有明確別名也沒有隱含別名的運算式是匿名的,查詢無法依名稱參照它們。
明確別名語法
您可以在 FROM
子句或 SELECT
清單中引入明確別名。
在 FROM
子句中,您可以使用 [AS] alias
為任何項目引入明確別名,包括表格、陣列、子查詢和 UNNEST
子句。AS
關鍵字是選用的。
範例
SELECT s.FirstName, s2.SongName
FROM Singers AS s JOIN Songs AS s2 ON s.SingerID = s2.SingerID;
您可以使用 [AS] alias
為 SELECT
清單中的任何運算式引入明確別名。AS
關鍵字是選用的。
範例
SELECT s.FirstName AS name, LOWER(s.FirstName) AS lname
FROM Singers s;
明確別名可見性
在查詢中引入明確別名後,對於您可以在查詢中的其他位置參照該別名的地方有限制。這些別名可見性的限制是 Beam 名稱範圍規則的結果。
FROM 子句別名
Beam 從左到右處理 FROM
子句中的別名,並且別名只對後續的 JOIN
子句可見。
不明確的別名
如果名稱不明確,也就是說它可以解析為多個唯一物件,則 Beam 會提供錯誤。
範例
此查詢包含表格之間衝突的欄位名稱,因為 Singers
和 Songs
都有名為 SingerID
的欄位
SELECT SingerID
FROM Singers, Songs;
隱含別名
在 SELECT
清單中,如果存在沒有明確別名的運算式,Beam 會根據以下規則指派隱含別名。SELECT
清單中可以有多個具有相同別名的欄位。
- 對於識別碼,別名是識別碼。例如,
SELECT abc
表示AS abc
。 - 對於路徑運算式,別名是路徑中的最後一個識別碼。例如,
SELECT abc.def.ghi
表示AS ghi
。 - 對於使用「點」成員欄位存取運算子的欄位存取,別名是欄位名稱。例如,
SELECT (struct_function()).fname
表示AS fname
。
在所有其他情況下,沒有隱含別名,因此欄位是匿名的,並且無法依名稱參照。該欄位的資料仍會傳回,且顯示的查詢結果可能會為該欄位產生標籤,但該標籤無法像別名一樣使用。
在 FROM
子句中,from_item
不需要具有別名。適用以下規則
如果存在沒有明確別名的運算式,Beam 會在以下情況下指派隱含別名
- 對於識別碼,別名是識別碼。例如,
FROM abc
表示AS abc
。 - 對於路徑運算式,別名是路徑中的最後一個識別碼。例如,
FROM abc.def.ghi
表示AS ghi
表格子查詢沒有隱含別名。
FROM UNNEST(x)
沒有隱含別名。
此頁面的部分內容是根據 work 修改而來,這些內容由 Google 共用,並依照 Creative Commons 3.0 屬名授權條款中所述的條款使用。