Beam ZetaSQL クエリ構文
クエリステートメントは、1 つ以上のテーブル、ストリーム、または式をスキャンし、計算された結果行を返します。
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 star と呼ばれることが多く、クエリ全体を実行した後に可視になる列ごとに 1 つの出力列を生成します。
SELECT * FROM (SELECT "apple" AS fruit, "carrot" AS vegetable);
+-------+-----------+
| fruit | vegetable |
+-------+-----------+
| apple | carrot |
+-------+-----------+
SELECT 式
注意: 最上位の SELECT
では、明示的に選択した列名を使用するか、式を使用する場合は明示的なエイリアスを使用する必要があります。
SELECT
リストの項目は式にすることができます。これらの式は単一の値に評価され、オプションの明示的な alias
を持つ 1 つの出力列を生成します。
式に明示的なエイリアスがない場合は、可能な場合は暗黙的なエイリアスのルールに従って暗黙的なエイリアスを受け取ります。それ以外の場合、列は匿名になり、クエリの他の場所で名前で参照することはできません。
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 ] 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
句は同じ名前の永続的なテーブルを非表示にします。例:
db.Roster
.
サブクエリ
サブクエリは、別のステートメントの内側に表示されるクエリで、かっこで囲まれて記述されます。これらは「サブ SELECT」または「ネストされた SELECT」とも呼ばれます。完全な SELECT
構文はサブクエリで有効です。
サブクエリには 2 つのタイプがあります。
- 式サブクエリ。式が有効なクエリで使用できます。式サブクエリは単一の値を返します。
- テーブルサブクエリ。
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
句は 2 つの from_item
をマージし、SELECT
句が 1 つのソースとしてクエリできるようにします。join_type
と ON
句 (「結合条件」) は、2 つの from_item
から行を結合および破棄して 1 つのソースを形成する方法を指定します。
すべての JOIN
句には join_type
が必要です。
[INNER] JOIN
INNER JOIN
または単に JOIN
は、2 つの 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
2 つの from_item
の LEFT OUTER JOIN
(または単に LEFT JOIN
) の結果は、結合述語を満たす右の from_item
に行がない場合でも、JOIN
句内の左の 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
が含まれます。結合された行 (2 つの行を結合した結果) は、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
は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
句が結果セット内の集計された行ごとに1回評価されることを意味します。これは、GROUP BY
と集計の前に評価される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
句自体に存在する必要はありませんが、集計は次のいずれかの形式で少なくとも1つ存在する必要があります。
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
句の両方に存在する場合、集計関数とそれらが参照する列は同じである必要はありません。以下の例では、2つの集計関数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
は、INT64型の負でないcount
を指定し、count
行以下の行が返されます。LIMIT
0
は0行を返します。セット演算がある場合、LIMIT
はセット演算が評価された後に適用されます。
OFFSET
は、INT64型の負でないskip_rows
を指定し、テーブル内のそのオフセットからの行のみが考慮されます。
これらの句は、リテラル値またはパラメーター値のみを受け入れます。
LIMIT
およびOFFSET
によって返される行は、これらの演算子がORDER BY
の後に使用されない限り、指定されません。
WITH 句
WITH
句は、1つ以上の名前付きサブクエリの結果を一時テーブル名にバインドします。導入された各テーブル名は、同じクエリ式内の後続の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 句での明示的なエイリアス
これら3つの句、GROUP BY
、ORDER BY
、およびHAVING
は、次の値のみを参照できます。
FROM
句内のテーブルとその任意の列。SELECT
リストのエイリアス。
GROUP BY
およびORDER BY
は、3番目のグループを参照することもできます。
SELECT
リスト内の項目を参照する整数リテラル。整数1
はSELECT
リストの最初の項目を、2
は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;
このクエリには、SELECT
リストで重複しているため、GROUP BY
句で曖昧なエイリアスが含まれています。
SELECT FirstName AS name, LastName AS name,
FROM Singers
GROUP BY name;
GROUP BY
内のFROM
句の列名とSELECT
リストのエイリアス間の曖昧さ
SELECT UPPER(LastName) AS LastName
FROM Singers
GROUP BY LastName;
上記のクエリは曖昧であり、GROUP BY
句のLastName
がSingers
の元の列LastName
を参照するか、値がUPPER(LastName)
であるエイリアスAS 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: サンプルデータを使った例
サンプルテーブル
次の3つのテーブルには、アスリート、その学校、およびシーズン中に獲得したポイントに関するサンプルデータが含まれています。これらのテーブルは、さまざまなクエリ句の動作を説明するために使用されます。
テーブル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 | マスコット |
---|---|
50 | ジャガーズ |
51 | ナイツ |
52 | レイカーズ |
53 | マスタングス |
TeamMascotテーブルには、ユニークな学校ID(SchoolID)と、その学校のマスコット(Mascot)のリストが含まれています。
JOIN タイプ
1) [INNER] JOIN
例
SELECT * FROM Roster JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;
結果
LastName | Roster.SchoolId | TeamMascot.SchoolId | マスコット |
---|---|---|---|
Adams | 50 | 50 | ジャガーズ |
Buchanan | 52 | 52 | レイカーズ |
Coolidge | 52 | 52 | レイカーズ |
Davis | 51 | 51 | ナイツ |
2) FULL [OUTER] JOIN
例
SELECT * FROM Roster FULL JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;
LastName | Roster.SchoolId | TeamMascot.SchoolId | マスコット |
---|---|---|---|
Adams | 50 | 50 | ジャガーズ |
Buchanan | 52 | 52 | レイカーズ |
Coolidge | 52 | 52 | レイカーズ |
Davis | 51 | 51 | ナイツ |
Eisenhower | 77 | NULL | NULL |
NULL | NULL | 53 | マスタングス |
3) LEFT [OUTER] JOIN
例
SELECT * FROM Roster LEFT JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;
結果
LastName | Roster.SchoolId | TeamMascot.SchoolId | マスコット |
---|---|---|---|
Adams | 50 | 50 | ジャガーズ |
Buchanan | 52 | 52 | レイカーズ |
Coolidge | 52 | 52 | レイカーズ |
Davis | 51 | 51 | ナイツ |
Eisenhower | 77 | NULL | NULL |
4) RIGHT [OUTER] JOIN
例
SELECT * FROM Roster RIGHT JOIN TeamMascot
ON Roster.SchoolID = TeamMascot.SchoolID;
結果
LastName | Roster.SchoolId | TeamMascot.SchoolId | マスコット |
---|---|---|---|
Adams | 50 | 50 | ジャガーズ |
Davis | 51 | 51 | ナイツ |
Coolidge | 52 | 52 | レイカーズ |
Buchanan | 52 | 52 | レイカーズ |
NULL | NULL | 53 | マスタングス |
GROUP BY 句
例
SELECT LastName, SUM(PointsScored)
FROM PlayerStats
GROUP BY LastName;
LastName | SUM |
---|---|
Adams | 7 |
Buchanan | 13 |
Coolidge | 1 |
セット演算子
UNION
UNION
演算子は、2つ以上のSELECT
ステートメントの結果セットを、各SELECT
ステートメントの結果セットから列をペアリングし、それらを垂直方向に連結することによって結合します。
例
SELECT Mascot AS X, SchoolID AS Y
FROM TeamMascot
UNION ALL
SELECT LastName, PointsScored
FROM PlayerStats;
結果
X | Y |
---|---|
ジャガーズ | 50 |
ナイツ | 51 |
レイカーズ | 52 |
マスタングス | 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)