Operatoren für die Abfrageausführung

Einleitung

Auf dieser Seite werden Details zu Operatoren beschrieben, die in Abfrageausführungsplänen für Spanner verwendet werden. Informationen zum Abrufen eines Ausführungsplans für eine bestimmte Abfrage über die Google Cloud Console finden Sie unter Wie Abfragen von Spanner ausgeführt werden.

Die Abfragen und Ausführungspläne auf dieser Seite basieren auf dem folgenden Datenbankschema:

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);

Sie können die folgenden DML-Anweisungen (Data Manipulation Language) verwenden, um diesen Tabellen Daten hinzuzufügen:

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");

Blattoperatoren

Ein Blattoperator ist ein Operator, der keine untergeordneten Elemente hat. Diese Arten von Blattoperatoren gibt es:

Array Unnest

Der Operator Array Unnest vereinfacht ein Eingabe-Array so, dass es in Zeilen von Elementen vorliegt. Jede resultierende Zeile enthält bis zu zwei Spalten: den tatsächlichen Wert aus dem Array und optional die nullbasierte Position im Array.

Wenn Sie zum Beispiel diese Abfrage verwenden:

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

Wird das Array [1,2,3] in Spalte a vereinfacht und die Arrayposition in Spalte b angezeigt.

Das sind die Ergebnisse:

a b
1 0
2 1
3 2

Das ist der Ausführungsplan:

Operator "Array Unnest"

Generate Relation

Der Operator Generate Relation gibt null oder mehr Zeilen zurück.

Unit Relation

Unit Relation gibt eine Zeile zurück. Das ist ein Sonderfall des Operators Generate Relation.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT 1 + 2 AS Result;

Lautet das Ergebnis:

Result
3

Das ist der Ausführungsplan:

Operator "Relation Operator"

Empty Relation

Empty Relation gibt keine Zeilen zurück. Das ist ein Sonderfall des Operators Generate Relation.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT * FROM Albums LIMIT 0

Lautet das Ergebnis:

No results

Das ist der Ausführungsplan:

Operator "Empty Relation"

Scan

Der Operator Scan gibt Zeilen zurück, indem eine Zeilenquelle gescannt wird. Dies sind die Arten von Scan-Operatoren:

  • Table Scan: Es wird eine Tabelle gescannt.
  • Index Scan: Es wird ein Index gescannt.
  • Batch Scan: Es werden Zwischentabellen gescannt, die von anderen relationalen Operatoren erstellt wurden (z. B. eine Tabelle, die mit Distributed Cross Apply erstellt wurde).

Wann immer möglich wendet Spanner im Rahmen eines Scans einfache Prädikate auf Schlüssel an. Scans werden effizienter ausgeführt, wenn Prädikate angewendet werden, da der Scan nicht die gesamte Tabelle oder den gesamten Index lesen muss. Prädikate werden im Ausführungsplan im Format KeyPredicate: column=value angezeigt.

Im schlimmsten Fall muss eine Abfrage möglicherweise alle Zeilen in einer Tabelle nachschlagen. Dies führt zu einem vollständigen Scan und wird im Ausführungsplan als full scan: true angezeigt.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT s.LastName
FROM singers@{FORCE_INDEX=SingersByFirstLastName} AS s
WHERE s.FirstName = 'Catalina';

Das sind die Ergebnisse:

LastName
Smith

Das ist der Ausführungsplan:

Operator "Scan"

Im Ausführungsplan sendet der übergeordnete Operator Distributed Union Teilpläne an Remoteserver. Jeder Teilplan hat den Operator Serialize Result und den Operator "Index Scan". Das Prädikat Key Predicate: FirstName = 'Catalina' schränkt den Scan auf Zeilen im Index SingersByFirstLastname ein, die als FirstName den Wert Catalina haben. Die Ausgabe von Index Scan wird an den Operator "Serialize Result" zurückgegeben.

Unäre Operatoren

Ein unärer Operator hat nur ein einziges untergeordnetes relationales Element.

Diese unären Operatoren gibt es:

Aggregate

Der Operator Aggregate implementiert SQL-Anweisungen vom Typ GROUP BY und Aggregatfunktionen (z. B. COUNT). Eingaben für den Operator "Aggregate" werden logisch in Gruppen unterteilt, die in Schlüsselspalten angeordnet sind oder in einer einzigen Gruppe ausgegeben werden, wenn GROUP BY nicht vorhanden ist. Für jede Gruppe werden null oder mehr Zusammenfassungen berechnet.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT s.SingerId, AVG(s.duration) AS average, COUNT(*) AS count
FROM Songs AS s
GROUP BY SingerId;

Wird die Abfrage nach SingerId gruppiert und eine AVG-Zusammenfassung sowie eine COUNT-Zusammenfassung durchgeführt.

Das sind die Ergebnisse:

SingerId Durchschnitt Anzahl
3 278 1
2 225,875 8

Das ist der Ausführungsplan:

Operator "Aggregate"

Mit dem Operator "Aggregate" können streambasierte oder hashbasierte Zusammenfassungen erstellt werden. Der obige Ausführungsplan stellt eine streambasierte Zusammenfassung dar. Streambasierte Zusammenfassungen lesen bereits vorsortierte Eingaben aus (wenn GROUP BY vorhanden ist) und berechnen die Gruppen ohne Blockierung. Hashbasierte Zusammenfassungen erstellen Hashtabellen, um inkrementelle Zusammenfassungen mehrerer Eingabezeilen gleichzeitig bereitzuhalten. Streambasierte Zusammenfassungen sind schneller und benötigen weniger Speicher als hashbasierte Zusammenfassungen. Die Eingabe muss jedoch nach Schlüsselspalten oder sekundären Indexen sortiert sein.

Bei verteilten Szenarien kann der Operator "Aggregate" in ein lokales/globales Paar aufgeteilt werden. Jeder Remoteserver führt eine lokale Zusammenfassung seiner Eingabezeile durch und gibt dann seine Ergebnisse an den Stammserver zurück. Der Stammserver führt die globale Zusammenfassung durch.

Apply Mutations

Der Operator Apply Mutations wendet die Mutationen aus einer DML-Anweisung auf die Tabelle an. Es ist der oberste Operator in einem Abfrageplan für eine DML-Anweisung.

Wenn Sie zum Beispiel diese Abfrage verwenden:

DELETE FROM Singers
WHERE FirstName = 'Alice';

Das sind die Ergebnisse:

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

Das ist der Ausführungsplan:

Operator "Apply Mutations"

Create Batch

Der Operator Create Batch erstellt eine Batchsequenz seiner Eingabezeilen. Der Operator "Create Batch" wird normalerweise im Rahmen der Operation Distributed Cross Apply ausgeführt. Die Eingabezeilen können bei der Batchverarbeitung neu geordnet werden. Die Anzahl der Eingabezeilen, die bei der jeweiligen Ausführung des Batch-Operators zu einem Batch zusammengefasst werden, ist variabel.

Beim Operator Distributed Cross Apply finden Sie ein Beispiel für den Operator "Create Batch" in einem Ausführungsplan.

Compute

Der Operator compute erzeugt eine Ausgabe, indem er die Eingabezeilen ausliest und eine oder mehrere zusätzliche Spalten hinzufügt, die über skalare Ausdrücke berechnet werden. Beim Operator Union All finden Sie ein Beispiel des Operators "Compute" in einem Ausführungsplan.

Compute Struct

Der Operator Compute Struct erstellt eine Variable für eine Struktur, die Felder für alle Eingabespalten enthält.

Wenn Sie zum Beispiel diese Abfrage verwenden:

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;

Das sind die Ergebnisse:

FirstName Ohne Angabe
Alice [["Not About The Guitar","BLUES"]]

Das ist der Ausführungsplan:

Operator "Compute Struct"

Im Ausführungsplan erhält der Operator "Array Subquery" eine Eingabe vom Operator Distributed Union, der die Eingabe vom Operator "Compute Struct" empfängt. Der Operator "Compute Struct" erstellt eine Struktur aus den Spalten SongName und SongGenre der Tabelle Songs.

Filtern

Der Operator Filter liest alle Zeilen der Eingabe aus, wendet ein skalares Prädikat auf jede Zeile an und gibt dann nur die Zeilen zurück, die das Prädikat erfüllen.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT s.LastName FROM (SELECT s.LastName
FROM Singers AS s LIMIT 3) s
WHERE s.LastName LIKE 'Rich%';

Das sind die Ergebnisse:

LastName
Richards

Das ist der Ausführungsplan:

Operator "Filter"

Das Prädikat für Interpreten, deren Nachname mit Rich beginnt, wird als Filter implementiert. Die Eingabedaten des Filters sind die Ausgabedaten eines Indexscans. Die Ausgabedaten des Filters sind die Zeilen, bei denen LastName mit Rich beginnt.

Was die Leistung angeht, beeinflusst ein Filter direkt oberhalb von Scan die Geschwindigkeit, mit der Daten gelesen werden. Betrachten Sie zum Beispiel eine Tabelle mit dem Schlüssel k. Ein Filter mit dem Prädikat k = 5 direkt oberhalb eines Tabellenscans sucht nach Zeilen, die k = 5 entsprechen, ohne die gesamte Eingabe zu lesen. Das führt zu einer effizienteren Ausführung der Abfrage. Im obigen Beispiel liest der Operator "Filter" nur die Zeilen aus, für die das Prädikat WHERE s.LastName LIKE 'Rich%' erfüllt ist.

Filter Scan

Der Operator Filter Scan bildet immer die Basis einer Tabellen- oder Indexsuche. Gemeinsam mit dem Scan reduziert er die Anzahl der aus der Datenbank gelesenen Zeilen. Der resultierende Scan erfolgt normalerweise schneller als unter Verwendung eines Filters. Spanner wendet den Filterscan unter bestimmten Bedingungen an:

  • Suchbare Bedingung: Die suchbare Bedingung gilt, wenn Spanner eine bestimmte Zeile für den Zugriff in der Tabelle bestimmen kann. Im Allgemeinen geschieht das, wenn sich der Filter in einem Präfix des Primärschlüssels befindet. Wenn der Primärschlüssel beispielsweise aus Col1 und Col2 besteht, dann ist die Klausel WHERE, die explizite Werte für Col1 oder Col1 und Col2 enthält, suchbar. In diesem Fall liest Spanner Daten nur innerhalb des Schlüsselbereichs.
  • Restbedingung: Jede andere Bedingung, bei der Spanner den Scan auswerten kann, um die gelesene Datenmenge zu begrenzen.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT LastName
FROM Singers
WHERE SingerId = 1

Das sind die Ergebnisse:

LastName
Richards

Das ist der Ausführungsplan:

Operator "Filter Scan"

Limit

Der Operator Limit beschränkt die Anzahl der zurückgegebenen Zeilen. Ein optionaler Parameter OFFSET gibt die erste Zeile an, die zurückgegeben werden soll. Bei verteilten Szenarien kann der Operator "Limit" in ein lokales/globales Paar aufgeteilt werden. Jeder Remoteserver führt eine lokale Begrenzung seiner Eingabezeilen durch und gibt dann seine Ergebnisse an den Stammserver zurück. Der Stammserver fasst die von den Remoteservern gesendeten Zeilen zusammen und wendet dann das globale Limit an.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT s.SongName
FROM Songs AS s
LIMIT 3;

Das sind die Ergebnisse:

SongName
Not About The Guitar
The Second Time
Starting Again

Das ist der Ausführungsplan:

Operator "Limit"

Das lokale Limit gilt jeweils für die Remoteserver. Der Stammserver fasst die Zeilen von den Remoteservern zusammen und wendet dann das globale Limit an.

Random Id Assign

Der Operator Random Id Assign generiert eine Ausgabe. Dazu liest er seine Eingabezeilen und fügt jeder Zeile eine Zufallszahl hinzu. Es funktioniert mit dem Operator Filter oder Sort, um Stichprobenmethoden zu erhalten. Die unterstützten Stichprobenverfahren sind Bernoulli und Reservoir.

Die folgende Abfrage verwendet zum Beispiel die Bernoulli-Stichprobenmethode mit einer Abtastrate von 10 %.

SELECT s.SongName
FROM Songs AS s TABLESAMPLE BERNOULLI (10 PERCENT);

Dies sind die Ergebnisse:

SongName
Starting Again
Nothing Is The Same

Beachten Sie, dass das Ergebnis bei jeder Ausführung variieren kann, obwohl die Abfrage dieselbe ist. Dies liegt daran, dass es sich bei dem Ergebnis um eine Stichprobe handelt.

Dies ist der Ausführungsplan:

Operator Bernoulli Sample

In diesem Ausführungsplan erhält der Operator Random Id Assign seine Eingabe vom Operator Distributed Union, der seine Eingabe von einer Indexsuche empfängt. Der Operator gibt die Zeilen mit zufälligen IDs zurück und der Operator Filter wendet dann ein Skalarprädikat für die Zufalls-IDs an und gibt etwa 10 % der Zeilen zurück.

Im folgenden Beispiel wird eine Reservoir-Stichprobenerfassung mit einer Stichprobenrate von zwei Zeilen verwendet.

SELECT s.SongName
FROM Songs AS s TABLESAMPLE RESERVOIR (2 ROWS);

Dies sind die Ergebnisse:

SongName
I Knew You Were Magic
The Second Time

Beachten Sie, dass das Ergebnis bei jeder Ausführung variieren kann, obwohl die Abfrage dieselbe ist. Dies liegt daran, dass es sich bei dem Ergebnis um eine Stichprobe handelt.

Dies ist der Ausführungsplan:

Operator „Reservoir Sample“

In diesem Ausführungsplan erhält der Operator Random Id Assign seine Eingabe vom Operator Distributed Union, der seine Eingabe von einer Indexsuche empfängt. Der Operator gibt die Zeilen mit zufälligen IDs zurück und der Operator Sort wendet dann die Sortierreihenfolge auf die zufälligen IDs an und wendet LIMIT mit zwei Zeilen an.

Serialize Result

Der Operator Serialize Result ist ein Spezialfall des Operators "Compute Struct", der jede Zeile des Endergebnisses der Abfrage serialisiert, um sie an den Client zurückzugeben.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT ARRAY(SELECT AS STRUCT so.SongName, so.SongGenre
             FROM Songs AS so
             WHERE so.SingerId = s.SingerId)
FROM Singers AS s;

fordert die Abfrage ein Array von SongName und SongGenre basierend auf SingerId an.

Das sind die Ergebnisse:

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

Das ist der Ausführungsplan:

Operator "Serialize Result"

Der Operator "Serialize Result" erzeugt ein Ergebnis, das für jede Zeile der Tabelle Singers ein Array von SongName- und SongGenre-Paaren für die Lieder des Sängers enthält.

Sort

Der Operator Sort liest die Eingabezeilen aus, sortiert sie nach Spalten und gibt dann die sortierten Ergebnisse zurück.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT s.SongGenre
FROM Songs AS s
ORDER By SongGenre;

Das sind die Ergebnisse:

SongGenre
BLUES
BLUES
BLUES
BLUES
KLASSIK
Land
ROCK
ROCK
ROCK

Das ist der Ausführungsplan:

Operator "Sort"

In diesem Ausführungsplan erhält der Operator "Sort" die Eingabezeilen vom Operator Distributed Union, sortiert die Eingabezeilen und gibt die sortierten Zeilen an den Operator Serialize Result zurück.

Um die Anzahl der zurückgegebenen Zeilen zu beschränken, kann der Operator "Sort" optional die Parameter LIMIT und OFFSET haben. Bei verteilten Szenarien wird der Operator "Sort" mit dem Operator LIMIT und/oder OFFSET in ein lokales/globales Paar aufgeteilt. Jeder Remoteserver führt die Sortierung der Eingabezeilen mit der Begrenzung und dem Startwert lokal durch und gibt dann seine Ergebnisse an den Stammserver zurück. Der Stammserver fasst die von den Remoteservern gesendeten Zeilen zusammen, sortiert sie und wendet dann global die Operatoren "Limit" und "Offset" an.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT s.SongGenre
FROM Songs AS s
ORDER By SongGenre
LIMIT 3;

Das sind die Ergebnisse:

SongGenre
BLUES
BLUES
BLUES

Das ist der Ausführungsplan:

Operator "Sort" mit Limit

Im Ausführungsplan wird das lokale Limit für die Remoteserver und das globale Limit für den Stammserver aufgeführt.

TVF

Der Operator Tabellenwertfunktion erzeugt eine Ausgabe, indem er die Eingabezeilen liest und die angegebene Funktion anwendet. Die Funktion könnte die Zuordnung implementieren und die gleiche Anzahl von Zeilen als Eingabe zurückgeben. Es kann auch ein Generator sein, der mehr Zeilen zurückgibt, oder ein Filter, der weniger Zeilen zurückgibt.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT Genre, SongName
FROM ML.PREDICT(MODEL GenreClassifier, Table Songs)

Dies sind die Ergebnisse:

Genre SongName
Land Not About The Guitar
Felsen The Second Time
Pop Starting Again
Pop Nothing Is The Same
Land Let's Get Back Together
Pop I Knew You Were Magic
Elektronische Musik Blau
Felsen 42
Felsen Fight Story

Dies ist der Ausführungsplan:

tvf-Operator

Union Input

Der Operator Union Input gibt Ergebnisse an den Operator Union All zurück. Beim Operator Union All finden Sie ein Beispiel des Operators "Union Input" in einem Ausführungsplan.

Binäre Operatoren

Ein binärer Operator hat zwei untergeordnete relationale Elemente. Diese binären Operatoren gibt es:

Cross Apply

Mit dem Operator "Cross Apply" wird eine Suchanfrage ausgeführt, mit der jede Zeile einer Tabelle abgerufen wird, die zuvor mit einer Suchanfrage aus einer anderen Tabelle abgerufen wurde. Anschließend wird die Schnittmenge der Suchdurchläufe durch alle Tabellen zurückgegeben. Im Gegensatz zu Operatoren mit datensatzbasierter Verarbeitung wie Hash Join führen die Operatoren "Cross Apply" und Outer Apply eine zeilenorientierte Verarbeitung durch. Der Operator "Cross Apply" erhält zwei Eingaben, Input und Map. Der Operator "Cross Apply" wendet jede von der Input-Seite erhaltene Zeile auf die von der Map-Seite erhaltenen Zeilen an. Das Ergebnis von Cross Apply enthält Spalten sowohl von Input als auch von Map.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT si.FirstName,
  (SELECT so.SongName
   FROM Songs AS so
   WHERE so.SingerId=si.SingerId
   LIMIT 1)
FROM Singers AS si;

Verlangt die Abfrage den Vornamen eines jeden Interpreten, zusammen mit dem Namen nur eines Titels des Interpreten.

Das sind die Ergebnisse:

FirstName Ohne Angabe
Alice Not About The Guitar
Catalina Let's Get Back Together
David NULL
Lea NULL
Marc NULL

Die erste Spalte wird mit Werten aus der Tabelle Singers und die zweite mit Werten aus der Tabelle Songs ausgefüllt. In den Fällen, in denen eine SingerId in der Tabelle Singers vorhanden war, aber keine übereinstimmende SingerId in der Tabelle Songs gefunden werden konnte, enthält die zweite Spalte NULL.

Das ist der Ausführungsplan:

Operator "Cross Apply"

Der übergeordnete Knoten ist der Operator Distributed Union. Der Operator "Distributed" Union verteilt Teilpläne an Remoteserver. Der Teilplan enthält den Operator Serialize Result, der den Vornamen des Interpreten und den Namen eines Titels des Interpreten berechnet und jede Ausgabezeile serialisiert.

Der Operator "Serialize Result" erhält seine Eingabe vom Operator "Cross Apply". Die Eingabeseite für den Operator "Cross Apply" stammt aus einem Scan der Tabelle Singers.

Die Eingabedaten unter Map für den Operator "Cross Apply" enthalten (von oben nach unten):

  • Den Operator Aggregate, der Songs.SongName zurückgibt
  • den Operator "Limit", der die Anzahl der zurückgegebenen Titel je Interpret begrenzt
  • Einen Indexscan des Index SongsBySingerAlbumSongNameDesc

Der Operator "Cross Apply" ordnet jede von Input erhaltene Zeile den von Map erhaltenen Zeilen mit derselben SingerId zu. Die Ausgabe des Operators "Cross Apply" setzt sich aus dem Wert FirstName aus der Eingabezeile von Input und dem Wert SongName aus der Eingabezeile von Map zusammen. Der Wert SongName lautet NULL, wenn es unter Map keine Zeile gibt, die der SingerId entspricht. Der im Ausführungsplan übergeordnete Operator "Distributed Union" fasst dann alle Ausgabezeilen von den Remoteservern zusammen und gibt sie als Abfrageergebnisse zurück.

Hash Join

Der Operator Hash Join ist eine hashbasierte Implementierung von SQL-Join. Hash Joins führen eine setbasierte Verarbeitung aus. Der Operator "Hash Join" liest die als Build markierten Eingabezeilen aus und fügt sie basierend auf einer Join-Bedingung in eine Hashtabelle ein. Der Operator "Hash Join" liest dann die als Probe markierten Eingabezeilen aus. Für jede Zeile, die der Operator "Hash Join" aus der "Probe"-Eingabe ausliest, sucht er nach übereinstimmenden Zeilen in der Hash-Tabelle. Der Operator "Hash Join" gibt die übereinstimmenden Zeilen als Ergebnis zurück.

Wenn Sie zum Beispiel diese Abfrage verwenden:

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;

Das sind die Ergebnisse:

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

Das ist der Ausführungsplan:

Operator "Hash Join"

Im Ausführungsplan ist Build ein Operator des Typs Distributed Union, der Scans auf die Tabelle Albums verteilt. Probe ist ein Operator des Typs Distributed Union, der Scans auf dem Index SongsBySingerAlbumSongNameDesc verteilt. Der Operator „Hash Join” liest alle Zeilen unter „Build” aus. Jede „Build”-Zeile wird basierend auf den Spalten der Bedingung a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId in einer Hashtabelle abgelegt. Als Nächstes liest der Operator „Hash Join” alle Zeilen unter „Probe” aus. Der Operator „Hash Join” sucht für jede „Probe”-Zeile nach Übereinstimmungen mit der Hashtabelle. Die resultierenden Übereinstimmungen werden vom Operator „Hash Join” zurückgegeben.

Resultierende Übereinstimmungen in der Hashtabelle können auch durch eine Residualbedingung gefiltert werden, bevor sie zurückgegeben werden. Ein Beispiel für Residualbedingungen sind Non-Equality Joins. Hash Join-Ausführungspläne können aufgrund von Speicherverwaltung und Join-Varianten komplex sein. Der Hauptalgorithmus eines Hash Join ist auf die Verarbeitung von Inner-, Semi-, Anti- und Outer Join-Varianten ausgelegt.

Merge join

Der Operator Merge Join ist eine zusammengeführte Implementierung des SQL Join. Beide Seiten des Joins erzeugen Zeilen, die nach den Spalten sortiert sind, die in der Join-Bedingung verwendet werden. Der Merge Join verarbeitet beide Eingabestreams gleichzeitig und gibt Zeilen aus, wenn die Join-Bedingung erfüllt ist. Wenn die Eingaben nicht ursprünglich nach Bedarf sortiert sind, fügt das Optimierungstool dem Plan explizite Sort-Operatoren hinzu.

Merge Join wird vom Optimierungstool nicht automatisch ausgewählt. Um diesen Operator zu verwenden, setzen Sie die Join-Methode im Abfragehinweis auf MERGE_JOIN, wie im folgenden Beispiel gezeigt:

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;

Dies sind die Ergebnisse:

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

Dies ist der Ausführungsplan:

Grafik: Operator_1 „Merge Join”

In diesem Ausführungsplan wird der Merge Join verteilt, sodass der Join an der Stelle ausgeführt wird, an der sich die Daten befinden. Dadurch kann der Merge Join in diesem Beispiel ohne die Einführung zusätzlicher Sortieroperatoren ausgeführt werden, da beide Tabellenscans bereits nach SingerId, AlbumId, der Join-Funktion-Bedingung, sortiert sind. In diesem Plan wird die linke Seite der Tabelle Albums immer dann gescannt, wenn deren SingerId, AlbumId vergleichsweise kleiner ist als die rechte Seite des Index-Scans SongsBySingerAlbumSongNameDesc, Paar SingerId_1, AlbumId_1. Ebenso wird die rechte Seite fortgesetzt, wenn sie kleiner als die linke Seite ist. Bei dieser Zusammenführung wird weiterhin nach Äquivalenzen gesucht, sodass mögliche Ergebnisse zurückgegeben werden können.

Betrachten Sie ein weiteres Beispiel für einen Merge Join mit der folgenden Abfrage:

SELECT a.AlbumTitle, s.SongName
FROM Albums AS a JOIN@{join_method=merge_join} Songs AS s
ON a.AlbumId = s.AlbumId;

Dies liefert die folgenden Ergebnisse:

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 Blau
Total Junk 42
Total Junk Not About The Guitar
Green The Second Time
Green Starting Again
Green Nothing Is The Same
Green Let\'s Get Back Together
Green I Knew You Were Magic
Green Blue
Green 42
Grün 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 Blau
Nothing To Do With Me 42
Nothing To Do With Me Not About The Guitar
Play The Second Time
Play Starting Again
Play Nothing Is The Same
Play Let\'s Get Back Together
Play I Knew You Were Magic
Play Blau
Play 42
Play Not About The Guitar
Terrified Fight Story

Dies ist der Ausführungsplan:

Operator_2 „Merge Join”

Im vorherigen Ausführungsplan wurden zusätzliche Sort-Operatoren vom Abfrageoptimierungstool eingeführt, um die erforderlichen Attribute zu erreichen, die für die Ausführung vom Merge Join erforderlich sind. Die Bedingung JOIN in der Abfrage dieses Beispiels bezieht sich nur auf AlbumId, wodurch die Daten nicht gespeichert werden. Daher muss eine Sortierung hinzugefügt werden. Die Abfrage-Engine unterstützt einen Algorithmus für verteiltes Zusammenführen, wodurch die Sortierung lokal und nicht global erfolgen kann und die CPU-Kosten verteilt und parallelisiert werden.

Die resultierenden Übereinstimmungen können auch durch eine Residualbedingung gefiltert werden, bevor sie zurückgegeben werden. Ein Beispiel für Residualbedingungen sind Non-Equality Joins. Aufgrund von zusätzlichen Sortieranforderungen können die Merge-Join-Ausführungspläne sehr komplex sein. Der Hauptalgorithmus eines Merge Join ist auf die Verarbeitung von Inner-, Semi-, Anti- und Outer Join-Varianten ausgelegt.

Push Broadcast Hash Join

Der Operator Push Broadcast Hash Join ist eine verteilte, Hash-Join-basierte Implementierung von SQL-Joins. Der Operator „Push Broadcast Hash Join” liest die Zeilen der Eingabeseite aus, um einen Datenbatch zu erstellen. Dieser Batch wird dann an alle Server gesendet, die Map-Nebendaten enthalten. Auf den Zielservern, auf denen der Batch von Daten empfangen wird, wird ein Hash Join erstellt, wobei der Batch als Build-Nebendaten erstellt und die lokalen Daten dann als Probeseite des Hash Joins gescannt werden.

Push Broadcast Hash Join wird vom Optimierungstool nicht automatisch ausgewählt. Um diesen Operator zu verwenden, legen Sie die Join-Methode für den Abfragehinweis auf PUSH_BROADCAST_HASH_JOIN fest, wie im folgenden Beispiel gezeigt:

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;

Dies sind die Ergebnisse:

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

Dies ist der Ausführungsplan:

Grafik: Operator „push_broadcast hash_join”

Die Eingabe für den Push Broadcast Hash Join ist der Index AlbumsByAlbumTitle. Diese Eingabe wird in einem Datenbatch serialisiert. Dieser Batch wird dann an alle lokalen Splits des Index SongsBySingerAlbumSongNameDesc gesendet, wo der Batch dann deserialisiert und in eine Hashtabelle eingebunden wird. Die Hashtabelle verwendet dann die lokalen Indexdaten als Prüfung, die resultierende Übereinstimmungen zurückgibt.

Resultierende Übereinstimmungen können auch durch eine Residualbedingung gefiltert werden, bevor sie zurückgegeben werden. Ein Beispiel für Residualbedingungen sind Non-Equality Joins.

Outer Apply

Der Operator Outer Apply entspricht ungefähr dem Operator Cross Apply. Allerdings wird mit einem Operator des Typs "Outer Apply" gewährleistet, dass bei jeder Ausführung auf der Map-Seite mindestens eine Zeile zurückgegeben wird, wobei bei Bedarf eine mit Nullen aufgefüllte Zeile erstellt wird. Mit anderen Worten bietet er die Semantik für "Left Outer Join".

n-stufige Operatoren

Ein n-stufiger Operator hat mehr als zwei untergeordnete relationale Elemente. Die folgenden n-stufigen Operatoren gibt es:

Union All

Der Operator Union All kombiniert alle Zeilensätze seiner untergeordneten Elemente, ohne Duplikate zu entfernen. "Union All"-Operatoren erhalten ihre Eingabe von Union Input-Operatoren, die auf mehrere Server verteilt sind. Der Operator "Union All" verlangt, dass seine Eingabedaten dasselbe Schema haben, also dieselben Datentypen für jede Spalte verwenden.

Wenn Sie zum Beispiel diese Abfrage verwenden:

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

Besteht der Zeilentyp für die untergeordneten Elemente aus zwei Ganzzahlen.

Das sind die Ergebnisse:

a b
1 2
3 4
5 6

Das ist der Ausführungsplan:

union_all_operator

Der Operator "Union All" kombiniert die Eingabezeilen und sendet in diesem Beispiel die Ergebnisse an einen Serialize Result-Operator.

Eine Abfrage wie die folgende wäre erfolgreich, da für jede Spalte derselbe Satz von Datentypen verwendet wird, obwohl die untergeordneten Elemente unterschiedliche Variablen für die Spaltennamen verwenden:

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

Eine Abfrage wie die folgende wäre nicht erfolgreich, da die untergeordneten Elemente unterschiedliche Datentypen für die Spalten verwenden:

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

Skalare Teilabfragen

Eine Skalare Teilabfrage ist ein SQL-Teilausdruck eines skalaren Ausdrucks. Spanner versucht, nach Möglichkeit skalare Unterabfragen zu entfernen. In bestimmten Szenarien können Pläne jedoch explizit skalare Teilabfragen enthalten.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT FirstName,
IF(FirstName='Alice',
   (SELECT COUNT(*)
    FROM Songs
    WHERE Duration > 300),
   0)
FROM Singers;

Ist dies der SQL-Teilausdruck:

SELECT COUNT(*)
FROM Songs
WHERE Duration > 300;

Dies sind die Ergebnisse der abgeschlossenen Abfrage:

FirstName
Alice 1
Catalina 0
David 0
Lea 0
Marc 0

Das ist der Ausführungsplan:

Operator "Scalar Subquery"

Der Ausführungsplan enthält eine skalare Teilabfrage, die als solche oberhalb des Operators Aggregate angezeigt wird.

Spanner konvertiert manchmal skalare Unterabfragen in einen anderen Operator, z. B. einen Join oder Cross Apply, um die Leistung möglicherweise zu verbessern.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT *
FROM Songs
WHERE Duration = (SELECT MAX(Duration) FROM Songs);

Ist dies der SQL-Teilausdruck:

SELECT MAX(Duration) FROM Songs;

Dies sind die Ergebnisse der abgeschlossenen Abfrage:

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

Das ist der Ausführungsplan:

Der Operator für eine skalare Teilabfrage wird nicht im Plan angezeigt

Der Ausführungsplan enthält keine skalare Unterabfrage, da Spanner die skalare Unterabfrage in einen Cross Apply-Vorgang konvertiert hat.

Array-Teilabfragen

Eine Array-Teilabfrage ähnelt einer skalaren Teilabfrage, mit der Ausnahme, dass die Teilabfrage mehr als eine Eingabezeile verbrauchen darf. Die verbrauchten Zeilen werden in ein einzelnes skalares Ausgabearray konvertiert, das pro verbrauchter Eingabezeile ein Element enthält.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT a.AlbumId,
ARRAY(SELECT ConcertDate
      FROM Concerts
      WHERE Concerts.SingerId = a.SingerId)
FROM Albums AS a;

Das Ist die Teilabfrage:

SELECT ConcertDate
FROM Concerts
WHERE Concerts.SingerId = a.SingerId;

Die Ergebnisse der Teilabfrage für jede AlbumId werden in ein Array von ConcertDate-Zeilen für diese AlbumId konvertiert. Der Ausführungsplan enthält eine Array-Teilabfrage, die als solche oberhalb eines "Distributed Union"-Operators angezeigt wird:

Operator "Array Subquery"

Verteilte Operatoren

Die bisher hier beschriebenen Operatoren werden jeweils nur auf einem einzelnen Gerät ausgeführt. Verteilte Operatoren werden auf mehreren Servern ausgeführt.

Folgende verteilte Operatoren gibt es:

Der Operator "Distributed Union" bildet die Basis, von der "Distributed Cross Apply" und "Distributed Outer Apply" abgeleitet werden.

Verteilte Operatoren treten in Ausführungsplänen mit einer Distributed Union-Variante über einer oder mehreren Local Distributed Union-Varianten auf. Eine "Distributed Union"-Variante führt die Remoteverteilung von Teilplänen durch. Eine lokale Variante von "Distributed Union" befindet sich über den einzelnen Scans, die für die Abfrage ausgeführt werden, wie in diesem Ausführungsplan gezeigt:

Operator "Distributed"

Die lokalen Varianten von "Distributed Union" stellen eine stabile Abfrageausführung sicher, wenn Neustarts zum dynamischen Ändern von Teilungsgrenzen auftreten.

Wann immer es möglich ist, hat eine Variante von "Distributed Union" ein geteiltes Prädikat, das zum "Split Pruning" führt, d. h., dass die Remoteserver Teilpläne nur für die Teilungen ausführen, die das Prädikat erfüllen. Dies verbessert sowohl die Latenzzeit als auch die Gesamtabfrageleistung.

Distributed Union

Der Operator Distributed Union teilt eine oder mehrere Tabellen konzeptionell in mehrere Splits auf, wertet eine Teilabfrage für jeden "Split" unabhängig aus und fasst dann alle Ergebnisse zusammen.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT s.SongName, s.SongGenre
FROM Songs AS s
WHERE s.SingerId = 2 AND s.SongGenre = 'ROCK';

Das sind die Ergebnisse:

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

Das ist der Ausführungsplan:

Operator "Distributed Union"

Der Operator "Distributed Union" sendet Teilpläne an die Remoteserver, die einen Tabellenscan der "Splits" ausführen, für die das Prädikat WHERE s.SingerId = 2 AND s.SongGenre = 'ROCK' erfüllt ist. Der Operator Serialize Result berechnet die Werte SongName und SongGenre aus den Zeilen, die von den Tabellenscans zurückgegeben werden. Der Operator "Distributed Union" gibt dann die kombinierten Ergebnisse von den Remoteservern zurück, wenn die SQL-Abfrageergebnisse vorliegen.

Distributed Merge Union

Der Operator Distributed Merge Union verteilt eine Abfrage auf mehrere Remoteserver. Anschließend werden die Abfrageergebnisse zu einem sortierten Ergebnis kombiniert, das als verteilte Zusammenführungssortierung bezeichnet wird.

Ein Distributed Merge Union führt die folgenden Schritte aus:

  1. Der Stammserver sendet eine Unterabfrage an jeden Remoteserver, der einen Split der abgefragten Daten hostet. Die Unterabfrage enthält Anweisungen, die die Ergebnisse in einer bestimmten Reihenfolge

  2. Jeder Remote-Server führt die Unterabfrage für seine Aufteilung aus und sendet dann die Ergebnisse in der angeforderten Reihenfolge zurück.

  3. Der Stammserver führt die sortierte Unterabfrage zusammen, um ein vollständig sortiertes Ergebnis zu erzeugen.

Distributed Merge Union ist für Spanner Version 3 und höher standardmäßig aktiviert.

Distributed Cross Apply

Der Operator Distributed Cross Apply (DCA) erweitert den Operator Cross Apply durch die Ausführung auf verschiedenen Servern. Auf der Input-Seite gruppiert DCA Zeilen-Batches, im Gegensatz zu einem regulären "Cross Apply"-Operator, der jeweils nur eine Eingabezeile behandelt. Auf der Map-Seite ist DCA eine Gruppe von "Cross Apply"-Operatoren, die auf Remoteservern ausgeführt werden.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT AlbumTitle FROM Songs
JOIN Albums ON Albums.AlbumId=Songs.AlbumId;

haben die Ergebnisse folgendes Format:

AlbumTitle
Green
Nothing To Do With Me
Play
Total Junk
Green

Das ist der Ausführungsplan:

Operator "Distributed Cross Apply"

Der DCA-Eingang enthält einen Index-Scan auf dem Index SongsBySingerAlbumSongNameDesc, der Reihen von AlbumId stapelt. Auf der Map-Seite besteht dieser "Cross Apply"-Operator aus einem Scan des Index AlbumsByAlbumTitle, abhängig vom Prädikat der AlbumId in der Eingabezeile, die mit dem Schlüssel AlbumId im Index AlbumsByAlbumTitle übereinstimmt. Die Zuordnung gibt den SongName für die SingerId-Werte in den zusammengeführten Eingabezeilen zurück.

Der DCA-Vorgang für dieses Beispiel im Überblick: Die Eingabedaten des DCA sind die zu Batches zusammengefassten Zeilen der Tabelle Albums und die Ausgabedaten ergeben sich aus der Anwendung dieser Zeilen auf die Zuordnung (Map) des Indexscans.

Distributed Outer Apply

Der Operator Distributed Outer Apply erweitert den Operator "Outer Apply" durch die Ausführung auf verschiedenen Servern. Das ähnelt der Vorgehensweise, mit der der Operator "Distributed Cross Apply" den Operator "Cross Apply" erweitert.

Wenn Sie zum Beispiel diese Abfrage verwenden:

SELECT LastName, ConcertDate FROM Singers
LEFT OUTER JOIN@{JOIN_TYPE=APPLY_JOIN} Concerts
ON Singers.SingerId=Concerts.SingerId;

haben die Ergebnisse folgendes Format:

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

Das ist der Ausführungsplan:

Operator "Distributed Outer Apply"

Apply Mutations

Der Operator Apply Mutations wendet die Mutationen aus einer DML-Anweisung auf die Tabelle an. Es ist der oberste Operator in einem Abfrageplan für eine DML-Anweisung.

Wenn Sie zum Beispiel diese Abfrage verwenden:

DELETE FROM Singers
WHERE FirstName = 'Alice';

Das sind die Ergebnisse:

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

Das ist der Ausführungsplan:

Operator "Apply Mutations"

Weitere Informationen

In diesem Abschnitt werden Elemente beschrieben, die keine eigenständigen Operatoren sind, sondern Aufgaben ausführen, um einen oder mehrere der oben aufgeführten Operatoren zu unterstützen. Die hier beschriebenen Elemente sind technisch gesehen Operatoren, sie können jedoch nicht unabhängig in Ihrem Anfrageplan auftreten.

Struct Constructor

Mit Struct Constructor wird eine Struktur oder eine Auflistung von Feldern erstellt. Normalerweise wird eine Struktur für Zeilen erstellt, die mit dem Operator "Compute" generiert wurden. Der Operator "Struct Constructor" ist nicht eigenständig einsetzbar. Er kommt stattdessen innerhalb der Operatoren Compute Struct oder Serialize Result vor.

Als Teil des Operators "Compute Struct" erstellt "Struct Constructor" eine Struktur, damit die Spalten für die berechneten Zeilen einen einzigen Variablenverweis auf die Struktur verwenden können.

Als Teil des Operators "Serialize Result" erstellt "Struct Constructor" eine Struktur, um die Ergebnisse zu serialisieren.

Wenn Sie zum Beispiel diese Abfrage verwenden:

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

Das sind die Ergebnisse:

A
1

Das ist der Ausführungsplan:

Struct Constructor

Im Ausführungsplan werden Operatoren des Typs "Struct Constructor" jeweils innerhalb des Operators "Serialize Result" angezeigt.