查詢執行運算子

本頁面詳細說明 Spanner 查詢執行計畫中使用的運算子。如要瞭解如何使用 Google Cloud 主控台擷取特定查詢的執行計畫,請參閱瞭解 Spanner 如何執行查詢

本頁的查詢和執行計畫是根據下列資料庫結構定義:

CREATE TABLE Singers (
  SingerId   INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  SingerInfo BYTES(MAX),
  BirthDate  DATE
) PRIMARY KEY(SingerId);

CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);

CREATE TABLE Albums (
  SingerId        INT64 NOT NULL,
  AlbumId         INT64 NOT NULL,
  AlbumTitle      STRING(MAX),
  MarketingBudget INT64
) PRIMARY KEY(SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget);

CREATE TABLE Songs (
  SingerId  INT64 NOT NULL,
  AlbumId   INT64 NOT NULL,
  TrackId   INT64 NOT NULL,
  SongName  STRING(MAX),
  Duration  INT64,
  SongGenre STRING(25)
) PRIMARY KEY(SingerId, AlbumId, TrackId),
  INTERLEAVE IN PARENT Albums ON DELETE CASCADE;

CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC), INTERLEAVE IN Albums;

CREATE INDEX SongsBySongName ON Songs(SongName);

CREATE TABLE Concerts (
  VenueId      INT64 NOT NULL,
  SingerId     INT64 NOT NULL,
  ConcertDate  DATE NOT NULL,
  BeginTime    TIMESTAMP,
  EndTime      TIMESTAMP,
  TicketPrices ARRAY<INT64>
) PRIMARY KEY(VenueId, SingerId, ConcertDate);

您可以使用下列資料操縱語言 (DML) 陳述式,將資料新增至這些資料表:

INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate)
VALUES (1, "Marc", "Richards", "1970-09-03"),
       (2, "Catalina", "Smith", "1990-08-17"),
       (3, "Alice", "Trentor", "1991-10-02"),
       (4, "Lea", "Martin", "1991-11-09"),
       (5, "David", "Lomond", "1977-01-29");

INSERT INTO Albums (SingerId, AlbumId, AlbumTitle)
VALUES (1, 1, "Total Junk"),
       (1, 2, "Go, Go, Go"),
       (2, 1, "Green"),
       (2, 2, "Forever Hold Your Peace"),
       (2, 3, "Terrified"),
       (3, 1, "Nothing To Do With Me"),
       (4, 1, "Play");

INSERT INTO Songs (SingerId, AlbumId, TrackId, SongName, Duration, SongGenre)
VALUES (2, 1, 1, "Let's Get Back Together", 182, "COUNTRY"),
       (2, 1, 2, "Starting Again", 156, "ROCK"),
       (2, 1, 3, "I Knew You Were Magic", 294, "BLUES"),
       (2, 1, 4, "42", 185, "CLASSICAL"),
       (2, 1, 5, "Blue", 238, "BLUES"),
       (2, 1, 6, "Nothing Is The Same", 303, "BLUES"),
       (2, 1, 7, "The Second Time", 255, "ROCK"),
       (2, 3, 1, "Fight Story", 194, "ROCK"),
       (3, 1, 1, "Not About The Guitar", 278, "BLUES");

Leaf 運算子

「leaf」運算子是沒有子項的運算子。leaf 運算子的類型如下:

Array unnest

「array unnest」運算子會將輸入陣列整併到元素資料列中。每個結果資料列最多包含兩個資料欄:來自陣列的實際值和/或陣列中以零為基礎的位置。

例如使用這個查詢:

SELECT a, b FROM UNNEST([1,2,3]) a WITH OFFSET b;

查詢會將資料欄 a 中的陣列 [1,2,3] 整併,並且將陣列的位置顯示在資料欄 b 中。

結果會是:

a b
1 0
2 1
3 2

以下為執行計畫:

array unnest 運算子

Generate relation

「generate relation」運算子會傳回零或多個資料列。

Unit relation

「unit relation」會傳回一個資料列。這是「generate relation」運算子的特殊案例。

例如使用這個查詢:

SELECT 1 + 2 AS Result;

結果會是:

Result
3

以下為執行計畫:

unit relation 運算子

Empty relation

「empty relation」不會傳回資料列。這是「generate relation」運算子的特殊案例。

例如使用這個查詢:

SELECT *
FROM   albums
LIMIT  0

結果會是:

沒有結果

以下為執行計畫:

empty relation 運算子

掃描

「scan」運算子會藉由掃描資料列來源的方式傳回資料列。以下是掃描運算子的類型:

  • Table scan:掃描會在資料表上進行。
  • Index scan:掃描會在索引上進行。
  • Batch scan:掃描會在其他關聯運算子建立的中繼資料表 (例如由分散式 cross apply 所建立的資料表) 上進行。

Spanner 會盡可能在索引鍵上套用述詞,做為掃描的一部分。套用了述詞的掃描能更有地效率執行,因為掃描不需要讀取整個資料表或索引。述詞在執行計畫中的形式為 KeyPredicate: column=value

在最糟的情況下,查詢可能需要查詢資料表中的所有資料列。這種情況會導致完整掃描,並在執行計畫中顯示為 full scan: true

例如使用這個查詢:

SELECT s.lastname
FROM   singers@{FORCE_INDEX=SingersByFirstLastName} as s
WHERE  s.firstname = 'Catalina';

結果會是:

LastName
Smith

以下為執行計畫:

scan 運算子

在執行計畫中,頂層的 distributed union 運算子會傳送子計畫到遠端伺服器。每個子計畫都有一個 serialize result 運算子和一個 index scan 運算子。述詞 Key Predicate: FirstName = 'Catalina' 將掃描限制在 SingersByFirstLastname 索引中,FirstName 等於 Catalina 的資料列。index scan 的輸出會傳回 serialize result 運算子。

Unary 運算子

「unary」運算子是包含單一相關子項的運算子。

以下運算子屬於 unary 運算子:

匯總

「aggregate」運算子會實作 GROUP BY SQL 陳述式並匯總函式 (例如 COUNT)。aggregate 運算子的輸入會以索引鍵資料欄有邏輯地分區為多個群組 (若沒有 GROUP BY,則為單一群組)。針對每個群組,系統會運算零或多個匯總。

例如使用這個查詢:

SELECT s.singerid,
       Avg(s.duration) AS average,
       Count(*)        AS count
FROM   songs AS s
GROUP  BY singerid;

查詢會以 SingerId 分組,並且執行 AVG 匯總與 COUNT 匯總。

結果會是:

SingerId average count
3 278 1
2 225.875 8

以下為執行計畫:

aggregate 運算子

Aggregate 運算子分成「串流型」或「雜湊型」兩類。上一個執行計畫顯示的是串流型匯總。串流型匯總會讀取預先排序的輸入 (如果有 GROUP BY),並在未封鎖的情況下運算群組。雜湊型匯總會建立雜湊表,以同時維護多個輸入列的遞增匯總。雖然與雜湊型匯總相比,串流型匯總的執行速度更快,也消耗較少的記憶體,但是串流型匯總需要使用經過排序的輸入 (依索引鍵欄或次要索引排序)。

在分散的情境中,系統可將 aggregate 運算子分隔成本機-全域組。每個遠端伺服器會在其輸入列執行本機匯總,然後將結果傳回根伺服器,讓根伺服器執行全域匯總。

Apply mutations

「apply mutations」運算子將來自於資料操縱陳述式 (DML) 的變異套用到資料表。它是 DML 陳述式查詢計畫的頂層運算子。

例如使用這個查詢:

DELETE FROM singers
WHERE  firstname = 'Alice';

結果會是:

 4 rows deleted  This statement deleted 4 rows and did not return any rows.

以下為執行計畫:

apply mutations 運算子

建立批次

「create batch」運算子會將輸入列分批,做成序列。建立批次作業通常是分散式 cross apply 作業的一部分。輸入列可以在批次作業期間重新排序。每次執行批次運算子,進行批次的輸入列數量會有所不同。

請參閱分散式 cross apply 運算子,取得執行計畫中 create batch 運算子的範例。

運算

「compute」運算子藉由讀取輸入列,並新增一或多個透過純量陳述式運算的額外資料欄來產生輸出。請參閱 union all 運算子,瞭解執行計畫中 compute 運算子的範例。

Compute struct

「compute struct」運算子會為結構建立變數,包含每個輸入資料欄的欄位。

例如使用這個查詢:

SELECT FirstName,
       ARRAY(SELECT AS STRUCT song.SongName, song.SongGenre
             FROM Songs AS song
             WHERE song.SingerId = singer.SingerId)
FROM singers AS singer
WHERE singer.SingerId = 3;

結果會是:

FirstName Unspecified
Alice [["Not About The Guitar","BLUES"]]

以下為執行計畫:

compute struct 運算子

在執行計畫中,array subquery 運算子會從 distributed union 運算子接收輸入,而該運算子會從 compute struct 運算子接收輸入。compute struct 運算子會從 Songs 資料表中的 SongNameSongGenre 資料欄建立結構。

DataBlockToRowAdapter

Spanner 查詢最佳化工具會在使用不同執行方法的一對運算子之間,自動插入 DataBlockToRowAdapter 運算子。其輸入內容是使用批次導向執行方法的運算子,輸出內容則會饋送至以列為導向的執行方法中執行的運算子。詳情請參閱「最佳化查詢執行作業」。

篩選器

「filter」運算子會讀取輸入的所有資料列,在每一列上套用純量述詞,然後只傳回滿足述詞的資料列。

例如使用這個查詢:

SELECT s.lastname
FROM   (SELECT s.lastname
        FROM   singers AS s
        LIMIT  3) s
WHERE  s.lastname LIKE 'Rich%';

結果會是:

LastName
Richards

以下為執行計畫:

filter 運算子

我們將以 Rich 開頭的歌手姓氏的述詞做為篩選器。篩選器的輸入是索引 scan 的輸出,而篩選的輸出是 LastName 開頭為 Rich 的資料列。

為了獲致效能,每當 filter 直接位於 scan 上方時,篩選器會影響資料讀取的方式。舉例來說,假設有一個包含索引鍵 k 的資料表。當包含述詞 k = 5 的 filter 直接位於 table scan 的上方,filter 會尋找符合 k = 5 的資料列,而不會讀取整個輸入。這讓執行查詢更有效率。在上一個範例中,filter 運算子只會讀取滿足 WHERE s.LastName LIKE 'Rich%' 述詞的資料列。

Filter scan

「filter scan」運算子總是位於 table scan 或 index scan 的上方。它會與掃描作業搭配使用,以減少從資料庫讀取的資料列數,而這樣的掃描通常會比使用 filter 更快。在某些條件下,Spanner 會套用 filter scan:

  • 可搜尋的條件:若 Spanner 可決定要存取資料表中的哪個資料列,便適用可搜尋的條件。一般而言,這會在 filter 位於主鍵的前置字串時發生。舉例來說,如果主鍵包含 Col1Col2,那麼包含明確的 Col1 值的 WHERE 子句或 Col1Col2 便是可搜尋的。在這種情況下,Spanner 只會讀取索引鍵範圍內的資料。
  • 其餘條件:其他 Spanner 可評估掃描,以限制讀取資料量的條件。

例如使用這個查詢:

SELECT lastname
FROM   singers
WHERE  singerid = 1

結果會是:

LastName
Richards

以下為執行計畫:

filter scan 運算子

限制

「limit」運算子會限制傳回的資料列數。選用的 OFFSET 參數會指定要傳回的起始列。在分散的情境中,系統可將 limit 運算子分隔成本地-全域組。每個遠端伺服器會套用輸出資料列的本機限制,然後將結果傳回根伺服器。根伺服器會匯總遠端伺服器傳送的資料列,然後套用全域限制。

例如使用這個查詢:

SELECT s.songname
FROM   songs AS s
LIMIT  3;

結果會是:

SongName
Not About The Guitar
The Second Time
Starting Again

以下為執行計畫:

limit 運算子

每個遠端伺服器的限制都會採用本機限制。根伺服器會從遠端伺服器匯總資料列,然後套用全域限制。

隨機 ID 指派

「random ID assign」運算子會讀取輸入列,並為每個資料列新增隨機數字,產生輸出內容。它可搭配 FilterSort 運算子使用,以實現取樣方法。支援的取樣方法為 BernoulliReservoir

例如,以下查詢使用 Bernoulli 取樣,取樣率為 10%。

SELECT s.songname
FROM   songs AS s TABLESAMPLE bernoulli (10 PERCENT);

結果會是:

SongName
Starting Again
Nothing Is The Same

請注意,由於結果是樣本,即使查詢相同,每次執行查詢的結果可能都會不同。

以下為執行計畫:

伯努利樣本運算子

在此執行計畫中,Random Id Assign 運算子會從 distributed union 運算子接收輸入,而該運算子會從 index scan 接收輸入。運算子會傳回具有隨機 ID 的資料列,然後 Filter 運算子會在隨機 ID 上套用純量述詞,並傳回約 10% 的資料列。

以下範例使用 Reservoir 取樣,取樣率為 2 列。

SELECT s.songname
FROM   songs AS s TABLESAMPLE reservoir (2 rows);

結果會是:

SongName
I Knew You Were Magic
The Second Time

請注意,由於結果是樣本,即使查詢相同,每次執行查詢的結果可能都會不同。

以下為執行計畫:

水庫樣本運算子

在此執行計畫中,Random Id Assign 運算子會從 distributed union 運算子接收輸入,而該運算子會從 index scan 接收輸入。運算子會傳回具有隨機 ID 的資料列,然後 Sort 運算子會對隨機 ID 套用排序順序,並套用具有 2 個資料列的 LIMIT

本機分割聯集

「local split union」運算子會找出儲存在本機伺服器上的資料表「分割」,對每個分割執行子查詢,然後建立聯集來合併所有結果。

掃描刊登位置資料表的執行計畫中,會顯示本機分割聯集。放置位置可增加資料表中的分割數量,讓系統更有效率地根據實體儲存位置掃描分割項目。

舉例來說,假設 Singers 資料表使用刊登位置鍵來分割歌手資料:

CREATE TABLE Singers (
    SingerId INT64 NOT NULL,
    SingerName STRING(MAX) NOT NULL,
    ...
    Location STRING(MAX) NOT NULL PLACEMENT KEY
) PRIMARY KEY (SingerId);

請思考以下查詢:

SELECT BirthDate FROM Singers;

以下為執行計畫:

本機分割聯集運算子

分散聯集會向每批實體儲存在同一台伺服器中的分割區塊傳送子查詢。在每部伺服器上,本機分割聯集會尋找儲存 Singers 資料的分割,對每個分割執行子查詢,然後傳回合併的結果。如此一來,分散式聯集和本機分割聯集就能共同運作,有效率地掃描 Singers 資料表。如果沒有本機分割聯集,分散式聯集會為每個分割傳送一個 RPC,而非每個分割批次,導致每個批次有多個分割時,RPC 來回傳輸會變得冗餘。

RowToDataBlockAdapter

Spanner 查詢最佳化工具會在使用不同執行方法的一對運算子之間,自動插入 RowToDataBlockAdapter 運算子。其輸入內容是使用以列為導向的執行方法的運算子,而輸出內容會饋送至以批次為導向的執行方法中執行的運算子。詳情請參閱「最佳化查詢執行作業」。

Serialize result

「serialize result」運算子是 compute struct 運算子的特殊案例,它會序列化查詢最終結果的每個資料列,以傳回用戶端。

例如使用這個查詢:

SELECT array
  (
    select as struct so.songname,
            so.songgenre
    FROM   songs AS so
    WHERE  so.singerid = s.singerid)
FROM  singers AS s;

查詢會依據 SingerId 要求 SongNameSongGenre 陣列。

結果會是:

Unspecified
[]
[[Let's Get Back Together, COUNTRY], [Starting Again, ROCK]]
[[Not About The Guitar, BLUES]]
[]
[]

以下為執行計畫:

Serialize result 運算子

serialize result 運算子會為 Singers 資料表的每個資料列建立結果,其中包含歌手歌曲的 SongNameSongGenre 組合陣列。

排序

「sort」運算子會讀取輸入列、依資料欄排序資料列,然後傳回排序的結果。

例如使用這個查詢:

SELECT s.songgenre
FROM   songs AS s
ORDER  BY songgenre;

結果會是:

SongGenre
BLUES
BLUES
BLUES
BLUES
CLASSICAL
國家/地區
ROCK
ROCK
ROCK

以下為執行計畫:

sort 運算子

在此執行計畫中,sort 運算子會從 distributed union 運算子接收輸入列、將輸入列排序,然後將已排序的資料列傳回 serialize result 運算子。

若要限制傳回的資料列數,可選擇在 sort 運算子包含 LIMITOFFSET 參數。針對分散式的情境,系統會將具備 LIMITOFFSET 的 sort 運算子分成本機-全域組。每個遠端伺服器都會套用輸入列的排序順序和本機限制/偏移,然後將結果傳回根伺服器。根伺服器會匯總遠端伺服器傳送的資料列,在排序後套用全域限制/偏移。

例如使用這個查詢:

SELECT s.songgenre
FROM   songs AS s
ORDER  BY songgenre
LIMIT  3;

結果會是:

SongGenre
BLUES
BLUES
BLUES

以下為執行計畫:

有限制的 sort 運算子

此執行計畫顯示遠端伺服器的本機限制,以及根伺服器的全域限制。

TVF

「table valued function」運算子會讀取輸入列並套用指定函式,產生輸出內容。函式可能會實作對應,並傳回與輸入相同的資料列數量。也可以是傳回更多資料列的產生器,或是傳回較少資料列的篩選器。

例如使用這個查詢:

SELECT genre,
       songname
FROM   ml.predict(model genreclassifier, TABLE songs)

結果會是:

類型 SongName
國家/地區 Not About The Guitar
搖滾 The Second Time
流行 Starting Again
流行 Nothing Is The Same
國家/地區 Let's Get Back Together
流行 I Knew You Were Magic
電子 藍色
搖滾 42
搖滾 Fight Story

以下為執行計畫:

tvf 運算子

Union input

「union input」運算子會將結果傳回「union all」運算子。如需在執行計畫中使用 union input 運算子的範例,請參閱 union all 運算子。

Binary 運算子

「binary」運算子是包含兩個關聯子項的運算子。以下運算子屬於二進位運算子:

Cross apply

「cross apply」運算子會對在查詢其他資料表後所擷取的每個資料列執行資料表查詢,並傳回所有資料表查詢的聯集。Cross apply 和 outer apply 運算子會執行資料列導向的處理,不像其他執行以組合為基礎的處理程序的運算子 (如 hash join)。cross apply 運算子包含兩種輸入:inputmap。cross apply 運算子會將輸入端中的每個資料列套用到對應端。cross apply 的結果包含輸入端和對應端的資料欄。

例如使用這個查詢:

 SELECT si.firstname,
       (SELECT so.songname
        FROM   songs AS so
        WHERE  so.singerid = si.singerid
        LIMIT  1)
FROM   singers AS si;

此查詢會要求每個歌手的名字,以及該歌手其中一首歌曲的名稱。

結果會是:

FirstName Unspecified
Alice Not About The Guitar
Catalina Let's Get Back Together
David NULL
Lea NULL
Marc 空值

第一欄是從 Singers 資料表填入,第二欄則是從 Songs 資料表填入。當 Singers 資料表中有 SingerId,但 Songs 資料表中沒有對應的 SingerId 時,第二欄會包含 NULL

以下為執行計畫:

cross apply 運算子

頂層節點的運算子是 distributed union 運算子。distributed union 運算子會將子計畫分配到遠端伺服器。子計畫包含 serialize result 運算子,會運算歌手的名字和該歌手其中一首歌曲的名稱,並且針對輸出的每一個資料列進行序列化。

serialize result 運算子會從 cross apply 運算子接收輸入。cross apply 運算子的輸入端是在 Singers 資料表上的 table scan

cross apply 運算的對應端包含以下內容 (從上至下):

  • aggregate 運算子,傳回 Songs.SongName
  • limit 運算子,將傳回的歌曲數量限制為每位歌手一首。
  • SongsBySingerAlbumSongNameDesc 索引上進行索引掃描

cross apply 運算子會將輸入端的每個資料列,對應到對應端中具有相同 SingerId 的資料列。cross apply 運算子輸出是輸入列的 FirstName 值,以及對應列的 SongName 值。(如果沒有對應到 SingerId 的對應列,SongName 值會是 NULL)。接著,位於執行計畫頂層的 distributed union 運算子會結合所有來自遠端伺服器的輸出列,做為查詢結果傳回。

Hash join

「hash join」運算子是 SQL join 的雜湊實作。Hash join 執行以組合為基礎的處理程序。hash join 運算子會從標示為 build 的輸入讀取資料列,並根據 join 條件插入雜湊表。hash join 運算子接著會從標示為 probe 的輸入讀取資料列。針對每個從 probe 輸入讀取的資料列,hash join 運算子會在雜湊表中查詢相符的資料列。hash join 運算子會傳回相符的資料列做為結果。

例如使用這個查詢:

SELECT a.albumtitle,
       s.songname
FROM   albums AS a join@{join_method=hash_join} songs AS s
ON a.singerid = s.singerid
AND    a.albumid = s.albumid;

結果會是:

AlbumTitle SongName
Nothing To Do With Me Not About The Guitar
Green The Second Time
Green Starting Again
Green Nothing Is The Same
綠色 Let's Get Back Together
綠色 I Knew You Were Magic
Green Blue
Green 42
Terrified Fight Story

以下為執行計畫:

hash join 運算子

在執行計畫中,「build」分散式聯集,會將掃描分配到 Albums 資料表。「Probe」是分散式聯集運算子,可將掃描分配到 SongsBySingerAlbumSongNameDesc 索引上。hash join 運算子會讀取所有來自建構端的資料列。系統會根據 a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId 條件中的欄,將每個建構列放入雜湊表格。接著,hash join 運算子會從探測端讀取所有資料列。針對每個探測資料列,hash join 運算子會查詢雜湊表中相符的內容。hash join 運算子會傳回符合的結果。

雜湊表中相符的結果在傳回前,可能會由剩餘條件篩選。(剩餘條件出現的位置可能會在非相等的聯結)。由於記憶體管理和聯結變數,Hash join 執行計畫可能會很複雜。主要的 hash join 演算法會經過調整,以處理內部變數、半變數、反變數和外部聯結變數。

合併彙整

「merge join」運算子是 SQL join 的合併式實作項目。聯結的兩側會依據聯結條件中使用的欄產生資料列。合併彙整會同時使用兩個輸入串流,並在符合彙整條件時輸出資料列。如果輸入內容未按照要求進行初始排序,最佳化工具就會在企劃書中加入明確的 Sort 運算子。

最佳化工具不會自動選取「Merge Join」。如要使用這個運算子,請在查詢提示中將彙整方法設為 MERGE_JOIN,如以下範例所示:

SELECT a.albumtitle,
       s.songname
FROM   albums AS a join@{join_method=merge_join} songs AS s
ON     a.singerid = s.singerid
AND    a.albumid = s.albumid;

結果會是:

AlbumTitle SongName
綠色 The Second Time
Green Starting Again
Green Nothing Is The Same
綠色 Let's Get Back Together
綠色 I Knew You Were Magic
Green Blue
Green 42
Terrified Fight Story
Nothing To Do With Me Not About The Guitar

以下為執行計畫:

merge join operator_1

在這個執行計劃中,會分散合併彙整,以便在資料所在位置執行彙整作業。這也讓本例中的合併聯結能夠在不需要引入其他排序運算子的情況下運作,因為兩個資料表掃描作業都已依據 SingerIdAlbumId 進行排序,而這兩者就是聯結條件。在這個計畫中,只要 SingerIdAlbumId 相較於右側 SongsBySingerAlbumSongNameDesc 索引掃描 SingerId_1AlbumId_1 組合較小,Albums 表格的左側掃描作業就會推進。同樣地,當右側小於左側時,右側會往前進。這個合併進度會繼續搜尋相等項目,以便傳回相符的結果。

請參考以下使用以下查詢的合併彙整範例:

SELECT a.albumtitle,
       s.songname
FROM   albums AS a join@{join_method=merge_join} songs AS s
ON a.albumid = s.albumid;

這會產生以下結果:

AlbumTitle SongName
Total Junk The Second Time
Total Junk Starting Again
Total Junk Nothing Is The Same
Total Junk Let's Get Back Together
Total Junk I Knew You Were Magic
Total Junk 藍色
Total Junk 42
Total Junk Not About The Guitar
Green The Second Time
Green Starting Again
Green Nothing Is The Same
綠色 Let's Get Back Together
綠色 I Knew You Were Magic
Green Blue
Green 42
綠色 Not About The Guitar
Nothing To Do With Me The Second Time
Nothing To Do With Me Starting Again
Nothing To Do With Me Nothing Is The Same
Nothing To Do With Me Let's Get Back Together
Nothing To Do With Me I Knew You Were Magic
Nothing To Do With Me 藍色
Nothing To Do With Me 42
Nothing To Do With Me Not About The Guitar
播放 The Second Time
播放 Starting Again
播放 Nothing Is The Same
播放 Let's Get Back Together
播放 I Knew You Were Magic
播放 藍色
播放 42
播放 Not About The Guitar
Terrified Fight Story

以下為執行計畫:

merge join operator_2

在上述執行計畫中,查詢最佳化工具已引入額外的 Sort 運算子,以便取得執行合併彙整所需的必要屬性。這個範例查詢中的 JOIN 條件只適用於 AlbumId,而非資料的儲存方式,因此必須新增排序條件。查詢引擎支援分散式合併演算法,可讓排序作業在本機而非全域執行,進而分散及並行處理 CPU 成本。

相符的結果在傳回前,可能會由剩餘條件篩選。(剩餘條件出現的位置可能會在非相等的聯結)。由於需要額外的排序條件,Merge Join 執行計畫可能會很複雜。主要的 merge join 演算法會經過調整,以處理內部、半、反和外部彙整變數。

推送廣播雜湊彙整

「push broadcast hash join」運算子是 SQL 聯結的雜湊型分散式實作項目。push broadcast hash join 運算子會從輸入端讀取資料列,以便建構一批資料。接著,該批次會廣播至所有含有地圖端資料的伺服器。在接收批次資料的目的地伺服器上,系統會使用批次做為建構端資料建立雜湊運算的結合,然後掃描本機資料做為雜湊運算的探測端。

最佳化工具不會自動選取「Push broadcast hash join」。如要使用這個運算子,請在查詢提示中將彙整方法設為 PUSH_BROADCAST_HASH_JOIN,如以下範例所示:

SELECT a.albumtitle,
       s.songname
FROM   albums AS a join@{join_method=push_broadcast_hash_join} songs AS s
ON     a.singerid = s.singerid
AND    a.albumid = s.albumid;

結果會是:

AlbumTitle SongName
綠色 The Second Time
Green Starting Again
Green Nothing Is The Same
綠色 Lets Get Back Together
綠色 I Knew You Were Magic
Green Blue
Green 42
Terrified Fight Story
Nothing To Do With Me Not About The Guitar

以下為執行計畫:

push_broadcast hash_join 運算子

Push 廣播雜湊彙整的輸入內容為 AlbumsByAlbumTitle 索引。該輸入內容會序列化為一批資料。然後將該批次傳送至索引 SongsBySingerAlbumSongNameDesc 的所有本機分割,接著將批次反序列化並建構至雜湊表格。接著,雜湊表會使用本機索引資料做為探針,傳回相符的結果。

相符的結果在傳回前,可能會由剩餘條件篩選。(剩餘條件出現的位置可能會在非相等的聯結)。

Outer apply

「outer apply」運算子與 cross apply 運算子類似,但 outer apply 運算子會在必要時製造以 NULL 填補的資料列,藉此確保對應端每次執行都會傳回至少一個資料列。(換言之,它會提供 left outer join 語意)。

遞迴聯集

「recursive union」運算子會執行兩個輸入值的聯集,一個代表 base 情況,另一個代表 recursive 情況。這個函式會用於含有量化路徑周遊的圖表查詢。系統會先處理基礎輸入內容,且只處理一次。直到遞迴結束為止。當達到上限 (如果有指定) 或遞迴無法產生任何新結果時,遞迴就會終止。在以下範例中,Collaborations 資料表會新增至結構定義,並建立名為 MusicGraph 的資源圖。

CREATE TABLE Collaborations (
    SingerId INT64 NOT NULL,
    FeaturingSingerId INT64 NOT NULL,
    AlbumTitle STRING(MAX) NOT NULL,
) PRIMARY KEY(SingerId, FeaturingSingerId, AlbumTitle);

CREATE OR REPLACE PROPERTY GRAPH MusicGraph
    NODE TABLES(
        Singers
            KEY(SingerId)
            LABEL Singers PROPERTIES(
                BirthDate,
                FirstName,
                LastName,
                SingerId,
                SingerInfo)
            )
EDGE TABLES(
    Collaborations AS CollabWith
        KEY(SingerId, FeaturingSingerId, AlbumTitle)
        SOURCE KEY(SingerId) REFERENCES Singers(SingerId)
        DESTINATION KEY(FeaturingSingerId) REFERENCES Singers(SingerId)
        LABEL CollabWith PROPERTIES(
          AlbumTitle,
          FeaturingSingerId,
          SingerId),
);

下列圖表查詢會找出與特定歌手合作或與這些合作對象合作的歌手。

GRAPH MusicGraph
MATCH (singer:Singers {singerId:42})-[c:CollabWith]->{1,2}(featured:Singers)
RETURN singer.SingerId AS singer, featured.SingerId AS featured

遞迴聯集運算子

遞迴聯集運算子會篩選 Singers 表格,找出具有指定 SingerId 的歌手。這是 遞迴聯集的基本輸入內容。遞迴聯集的遞迴輸入內容包含分散式交叉套用或其他聯結運算子,適用於其他會重複將 Collaborations 資料表與前一個迭代聯結結果結合的查詢。基礎輸入內容的資料列會形成第 0 次迭代。在每次疊代時,遞迴暫存掃描會儲存疊代的輸出內容。遞迴式 spool 掃描的資料列會與 spoolscan.featuredSingerId = Collaborations.SingerId 上的 Collaborations 資料表聯結。兩次疊代完成後,遞迴就會結束,因為這是查詢中指定的上限。

N-ary 運算子

「N-ary」運算子是包含兩個以上兩個關聯子項的運算子。以下運算子屬於 N-ary 運算子:

Union all

「union all」運算子會結合所有子項的資料列組合,且不會移除重複項目。Union all 運算子從分散在多個伺服器上的 union input 運算子接收輸入。union all 運算子要求其輸入使用相同結構定義,亦即每個資料欄都有相同的資料類型組。

例如使用這個查詢:

SELECT 1 a,
       2 b
UNION ALL
SELECT 3 a,
       4 b
UNION ALL
SELECT 5 a,
       6 b;

子項的資料列類型包含兩個整數。

結果會是:

a b
1 2
3 4
5 6

以下為執行計畫:

union_all_operator

union all 運算子會整合其輸入列,在此例中它會傳送結果到 serialize result 運算子。

由於每個資料欄中皆使用相同的資料類型組合,即使子項使用不同的變數做為資料欄名稱,下列查詢仍會成功:

SELECT 1 a,
       2 b
UNION ALL
SELECT 3 c,
       4 e;

由於子項的資料欄使用不同的資料類型,下列查詢不會成功:

SELECT 1 a,
       2 b
UNION ALL
SELECT 3 a,
  'This is a string' b;

Scalar subquery

「scalar subquery」屬於 SQL 子陳述式,是純量運算式的一部分。Spanner 會盡可能嘗試移除純量子查詢。然而,在某些情境下,計畫可明確包含 scalar subquery。

例如使用這個查詢:

SELECT firstname,
  IF(firstname = 'Alice', (SELECT Count(*)
                          FROM   songs
                          WHERE  duration > 300), 0)
FROM   singers;

這是 SQL 子運算式:

SELECT Count(*)
FROM   songs
WHERE  duration > 300;

以下為 (完整查詢) 結果:

FirstName
Alice 1
Catalina 0
David 0
Lea 0
Marc 0

以下為執行計畫:

scalar subquery 運算子

執行計畫包含一個純量子查詢,以 Scalar Subquery 顯示在 aggregate 運算子上方。

Spanner 有時會將 scalar subquery 轉換成另一個運算子,如 join 或 cross join 以改善效能。

例如使用這個查詢:

SELECT *
FROM   songs
WHERE  duration = (SELECT Max(duration)
                   FROM   songs);

這是 SQL 子運算式:

SELECT MAX(Duration)
FROM Songs;

以下為 (完整查詢) 結果:

SingerId AlbumId TrackId SongName Duration SongGenre
2 1 6 Nothing Is The Same 303 BLUES

以下為執行計畫:

未顯示在計畫中的 scalar subquery 運算子

此執行計畫不包含 scalar subquery,因為 Spanner 已將 scalar subquery 轉換為 cross apply。

Array subquery

「array subquery」與 scalar subquery 類似,但 array subquery 可耗用一個以上的輸入列。耗用的資料列會轉換成一個純量輸出陣列,其中每個耗用的輸入列都包含一個元素。

例如使用這個查詢:

SELECT a.albumid,
       array
       (
              select concertdate
              FROM   concerts
              WHERE  concerts.singerid = a.singerid)
FROM   albums AS a;

這是子查詢:

SELECT concertdate
FROM   concerts
WHERE  concerts.singerid = a.singerid;

每個 AlbumId 的子查詢結果會轉換成對應於該 AlbumIdConcertDate 資料列陣列。此執行計畫包含 array subquery,顯示為 Array Subquery,位於 distributed union 運算子上方:

array subquery 運算子

分散式運算子

本頁稍早描述的運算子僅會在單一機器上執行。「分散式運算子」則會在多個伺服器中執行。

以下運算子屬於分散式運算子:

distributed union 運算子是從 distributed cross apply 和 distributed outer apply 擷取而來的原始運算子

執行計畫中的分散式運算子在一或多個本機 distributed union 變數的上方,會有一個 distributed union 變數。distributed union 變數會執行子計畫的遠端分配作業。本機 distributed union 變數會位於查詢所執行的每個掃描的上方,如以下執行計畫所示:

分散式運算子

當動態變更的分割界線重新啟動時,本機 distributed union 變數可確保查詢執行的穩定性。

distributed union 變數會儘可能包含可產生分割修剪的分割述詞,也就是遠端伺服器只會在滿足述詞的分割上執行子計畫。這會改善延遲時間和整體查詢效能。

Distributed union

「distributed union」運算子在概念上將一或多個資料表分到多個分割,並在每個分割上個別遠端評估子查詢,然後聯集所有結果。

例如使用這個查詢:

SELECT s.songname,
       s.songgenre
FROM   songs AS s
WHERE  s.singerid = 2
       AND s.songgenre = 'ROCK';

結果會是:

SongName SongGenre
Starting Again ROCK
The Second Time ROCK
Fight Story ROCK

以下為執行計畫:

distributed union 運算子

distributed union 運算子會傳送子計畫到遠端伺服器,跨分割執行資料表掃描以滿足查詢的述詞 WHERE s.SingerId = 2 AND s.SongGenre = 'ROCK'serialize result 運算子會從資料表掃描傳回的資料列運算 SongNameSongGenre 值。接著,distributed union 運算子便會傳回遠端伺服器的綜合結果,做為 SQL 查詢結果。

分散式合併聯集

分散式合併聯集運算子會將查詢分散至多個遠端伺服器。接著,系統會合併查詢結果,產生排序結果,這稱為「分散式合併排序」

分散式合併聯集會執行下列步驟:

  1. 根伺服器會將子查詢傳送至每個遠端伺服器,這些伺服器會代管查詢資料的分割資料。子查詢包含指示,說明結果會以特定順序排序。

  2. 每個遠端伺服器都會對分割資料執行子查詢作業,然後再以要求的順序傳回結果。

  3. 根伺服器會合併已排序的子查詢,產生完全排序的結果。

根據預設,Spanner 3 以上版本會啟用分散式合併聯集。

Distributed cross apply

「distributed cross apply」 (DCA) 運算子會跨多個伺服器執行,延伸 cross apply 運算子。DCA 輸入端會將資料列「批次」分組 (與一次只能處理一個輸入列的一般 cross apply 不同)。DCA 對應端是執行遠端伺服器的 cross apply 運算子組。

例如使用這個查詢:

SELECT albumtitle
FROM   songs
       JOIN albums
         ON albums.albumid = songs.albumid;

結果的格式如下:

AlbumTitle
Green
Nothing To Do With Me
Play
Total Junk
Green

以下為執行計畫:

distributed cross apply 運算子

DCA 輸入包含在 SongsBySingerAlbumSongNameDesc 索引上進行索引掃描,這項掃描會將 AlbumId 的資料列分批。此 cross apply 運算子的對應端是對 AlbumsByAlbumTitle 索引的索引掃描,受制於輸入列中符合 AlbumsByAlbumTitle 索引 AlbumId 鍵的 AlbumId 述詞。對應會在分批的輸入列中傳回 SingerId 值的 SongName

總結此範例的 DCA 程序,DCA 的輸入是 Albums 資料表中分批的列,而 DCA 的輸出就是這些資料列應用到索引掃描的對應。

Distributed outer apply

「distributed outer apply」運算子會藉由跨多個伺服器執行的方式延伸 outer apply 運算子,類似 distributed cross apply 運算子延伸 cross apply 運算子的方式。

例如使用這個查詢:

SELECT lastname,
       concertdate
FROM   singers LEFT OUTER join@{JOIN_TYPE=APPLY_JOIN} concerts
ON singers.singerid=concerts.singerid;

結果的格式如下:

LastName ConcertDate
Trentor 2014-02-18
Smith 2011-09-03
Smith 2010-06-06
Lomond 2005-04-30
Martin 2015-11-04
Richards

以下為執行計畫:

distributed outer apply 運算子

Apply mutations

「apply mutations」運算子將來自於資料操縱陳述式 (DML) 的變異套用到資料表。它是 DML 陳述式查詢計畫的頂層運算子。

例如使用這個查詢:

DELETE FROM singers
WHERE  firstname = 'Alice';

結果會是:

 4 rows deleted  This statement deleted 4 rows and did not return any rows.

以下為執行計畫:

apply mutations 運算子

其他資訊

本節說明不是獨立運算子的項目,但可執行工作以支援先前列出的一個或多個運算子。此處描述的項目雖然技術上算是運算子,卻不是查詢計畫中的獨立運算子。

結構建構函式

「結構建構函式」會建立「結構」,也就是一組欄位。一般而言,此函式會為源自於運算作業的資料列建立結構。結構體建構函式不是獨立的運算子,而會出現在 compute struct 運算子或 serialize result 運算子中。

針對運算結構作業,結構建構函式會建立結構,如此運算資料列的資料欄才可使用單一變數參照到建構。

針對序列化的結果作業,結構建構函式會建立結構以序列化結果。

例如使用這個查詢:

SELECT IF(TRUE, struct(1 AS A, 1 AS B), struct(2 AS A , 2 AS B)).A;

結果會是:

A
1

以下為執行計畫:

結構建構函式

在執行計畫中,結構建構函式會在 serialize result 運算子中出現。