Operatori di esecuzione delle query

Questa pagina descrive in dettaglio gli operatori utilizzati nei piani di esecuzione delle query di Spanner. Per scoprire come recuperare un piano di esecuzione per una query specifica utilizzando la console Google Cloud , consulta Informazioni su come Spanner esegue le query.

Le query e i piani di esecuzione in questa pagina si basano sul seguente schema del database:

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

Puoi utilizzare le seguenti istruzioni DML (Data Manipulation Language) per aggiungere dati a queste tabelle:

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

Operatori foglia

Un operatore foglia è un operatore che non ha elementi secondari. I tipi di operatori foglia sono:

Estrai array

Un operatore di estrazione di array appiattisce un array di input in righe di elementi. Ogni riga risultante contiene massimo due colonne: il valore effettivo dell'array e, facoltativamente, la posizione in base zero nell'array.

Ad esempio, utilizzando questa query:

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

La query appiattisce l'array [1,2,3] nella colonna a e mostra la posizione dell'array nella colonna b.

Ecco i risultati:

a b
1 0
2 1
3 2

Questo è il piano di esecuzione:

operatore di annidamento dell&#39;array

Genera relazione

Un operatore generate relation restituisce zero o più righe.

Relazione con unità

La relazione unità restituisce una riga. Si tratta di un caso speciale dell'operatore generate relation.

Ad esempio, utilizzando questa query:

SELECT 1 + 2 AS Result;

Il risultato è:

Risultato
3

Questo è il piano di esecuzione:

operatore di relazione unità

Relazione vuota

La relazione vuota non restituisce righe. Si tratta di un caso speciale dell'operatore generate relation.

Ad esempio, utilizzando questa query:

SELECT *
FROM   albums
LIMIT  0

Il risultato è:

Nessun risultato

Questo è il piano di esecuzione:

operatore di relazione vuoto

Scansione

Un operatore di scansione restituisce le righe eseguendo la scansione di un'origine di righe. Questi sono i tipi di operatori di scansione:

  • Scansione tabella: la scansione viene eseguita su una tabella.
  • Scansione dell'indice: la scansione viene eseguita su un indice.
  • Scansione batch: la scansione viene eseguita su tabelle intermedie create da altri operatori relazionali (ad esempio, una tabella creata da un cross-apply distribuito).

Quando possibile, Spanner applica i predicati alle chiavi nell'ambito di una scansione. Le scansioni vengono eseguite in modo più efficiente quando vengono applicati i predicati perché la scansione non deve leggere l'intera tabella o l'intero indice. I predicati vengono visualizzati nel piano di esecuzione nel formato KeyPredicate: column=value.

Nel peggiore dei casi, una query potrebbe dover cercare tutte le righe di una tabella. Questa situazione porta a una scansione completa e viene visualizzata nel piano di esecuzione come full scan: true.

Ad esempio, utilizzando questa query:

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

Ecco i risultati:

Cognome
Smith

Questo è il piano di esecuzione:

operatore di scansione

Nel piano di esecuzione, l'operatore distributed union di primo livello invia i sottopiani ai server remoti. Ogni piano secondario ha un operatore serialize result e un operatore di scansione dell'indice. Il predicato Key Predicate: FirstName = 'Catalina' limita la scansione alle righe dell'indice SingersByFirstLastname che hanno FirstName uguale a Catalina. L'output della scansione dell'indice viene restituito all'operatore di serializzazione dei risultati.

Operatori unari

Un operatore unario è un operatore che ha un solo elemento secondario relazionale.

I seguenti operatori sono operatori unari:

Aggregazione

Un operatore aggregato implementa istruzioni SQL GROUP BY e funzioni di aggregazione (come COUNT). L'input per un operatore aggregato viene partizionato logicamente in gruppi disposti in colonne chiave (o in un singolo gruppo se GROUP BY non è presente). Per ogni gruppo vengono calcolati zero o più aggregazioni.

Ad esempio, utilizzando questa query:

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

La query raggruppa per SingerId ed esegue un'aggregazione AVG e un'aggregazione COUNT.

Ecco i risultati:

SingerId media conteggio
3 278 1
2 225.875 8

Questo è il piano di esecuzione:

operatore di aggregazione

Gli operatori di aggregazione possono essere basati sul flusso o basati sull'hash. Il piano di esecuzione precedente mostra un aggregato basato sul flusso. Gli aggregati basati su stream leggono da input già preordinati (se è presente GROUP BY) e calcolano il gruppo senza blocchi. Gli aggregati basati su hash creano tabelle hash per mantenere aggregati incrementali di più righe di input contemporaneamente. Gli aggregati basati su stream sono più veloci e utilizzano meno memoria rispetto agli aggregati basati su hash, ma richiedono che l'input sia ordinato (in base alle colonne chiave o agli indici secondari).

Per gli scenari distribuiti, un operatore di aggregazione può essere separato in una coppia locale-globale. Ogni server remoto esegue l'aggregazione locale sulle proprie righe di input e poi restituisce i risultati al server radice. Il server radice esegue l'aggregazione globale.

Applica mutazioni

Un operatore apply mutations applica le mutazioni di un'istruzione DML (Data Manipulation Statement) alla tabella. È l'operatore principale in un piano di query per un'istruzione DML.

Ad esempio, utilizzando questa query:

DELETE FROM singers
WHERE  firstname = 'Alice';

Ecco i risultati:

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

Questo è il piano di esecuzione:

operatore apply mutations

Creazione batch

Un operatore create batch raggruppa le righe di input in una sequenza. Un'operazione di creazione batch si verifica di solito nell'ambito di un'operazione di applicazione incrociata distribuita. Le righe di input possono essere riordinate durante il batch. Il numero di righe di input raggruppate in batch in ogni esecuzione dell'operatore batch varia.

Consulta l'operatore cross-apply distribuito per un esempio di operatore di creazione batch in un piano di esecuzione.

Computing

Un operatore compute produce output leggendo le righe di input e aggiungendo una o più colonne aggiuntive calcolate utilizzando espressioni scalari. Consulta l'operatore Union All per un esempio di operatore di calcolo in un piano di esecuzione.

Compute struct

Un operatore compute struct crea una variabile per una struttura che contiene campi per ciascuna delle colonne di input.

Ad esempio, utilizzando questa query:

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;

Ecco i risultati:

Nome Non specificato
Alice [["Not About The Guitar","BLUES"]]

Questo è il piano di esecuzione:

operatore di struct di calcolo

Nel piano di esecuzione, l'operatore di sottoquery array riceve l'input da un operatore distributed union, che riceve l'input da un operatore compute struct. L'operatore di struttura di calcolo crea una struttura dalle colonne SongName e SongGenre nella tabella Songs.

DataBlockToRowAdapter

Un operatore DataBlockToRowAdapter viene inserito automaticamente dall'ottimizzatore di query Spanner tra una coppia di operatori che operano utilizzando metodi di esecuzione diversi. Il suo input è un operatore che utilizza il metodo di esecuzione orientato ai batch e il suo output viene inserito in un operatore che esegue il metodo di esecuzione orientato alle righe. Per maggiori informazioni, vedi Ottimizzare l'esecuzione delle query.

Filtro

Un operatore filter legge tutte le righe dell'input, applica un predicato scalare a ogni riga e restituisce solo le righe che soddisfano il predicato.

Ad esempio, utilizzando questa query:

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

Ecco i risultati:

Cognome
Richards

Questo è il piano di esecuzione:

operatore di filtro

Il predicato per i cantanti il cui cognome inizia con Rich è implementato come filtro. L'input del filtro è l'output di una scansione dell'indice e l'output del filtro sono le righe in cui LastName inizia con Rich.

Per migliorare il rendimento, ogni volta che un filtro viene posizionato direttamente sopra una scansione, il filtro influisce sulla modalità di lettura dei dati. Ad esempio, considera una tabella con la chiave k. Un filtro con il predicato k = 5 direttamente sopra una scansione della tabella cerca righe che corrispondono a k = 5, senza leggere l'intero input. Ciò comporta un'esecuzione più efficiente della query. Nell'esempio precedente, l'operatore di filtro legge solo le righe che soddisfano il predicato WHERE s.LastName LIKE 'Rich%'.

Scansione del filtro

Un operatore di scansione del filtro si trova sempre in cima a una scansione di tabella o indice. Funziona con la scansione per ridurre il numero di righe lette dal database e la scansione risultante è in genere più veloce rispetto a un filtro. Spanner applica la scansione del filtro in determinate condizioni:

  • Condizione di ricerca: la condizione di ricerca si applica se Spanner può determinare una riga specifica a cui accedere nella tabella. In generale, questo si verifica quando il filtro si trova su un prefisso della chiave primaria. Ad esempio, se la chiave primaria è costituita da Col1 e Col2, una clausola WHERE che include valori espliciti per Col1 o Col1 e Col2 è ricercabile. In questo caso, Spanner legge i dati solo all'interno dell'intervallo di chiavi.
  • Condizione residua: qualsiasi altra condizione in cui Spanner può valutare la scansione per limitare la quantità di dati letti.

Ad esempio, utilizzando questa query:

SELECT lastname
FROM   singers
WHERE  singerid = 1

Ecco i risultati:

Cognome
Richards

Questo è il piano di esecuzione:

operatore di scansione del filtro

Limite

Un operatore limit vincola il numero di righe restituite. Un parametro facoltativo OFFSET specifica la riga iniziale da restituire. Per gli scenari distribuiti, un operatore di limite può essere suddiviso in una coppia locale-globale. Ogni server remoto applica il limite locale alle proprie righe di output, quindi restituisce i risultati al server radice. Il server radice aggrega le righe inviate dai server remoti e poi applica il limite globale.

Ad esempio, utilizzando questa query:

SELECT s.songname
FROM   songs AS s
LIMIT  3;

Ecco i risultati:

SongName
Not About The Guitar
La seconda volta
Ricominciare

Questo è il piano di esecuzione:

operatore di limite

Il limite locale è il limite per ogni server remoto. Il server radice aggrega le righe dai server remoti e poi applica il limite globale.

Assegnazione ID casuale

Un operatore assegnazione ID casuale produce l'output leggendo le righe di input e aggiungendo un numero casuale a ogni riga. Funziona con un operatore Filter o Sort per ottenere metodi di campionamento. I metodi di campionamento supportati sono Bernoulli e Reservoir.

Ad esempio, la seguente query utilizza il campionamento di Bernoulli con una frequenza di campionamento del 10%.

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

Ecco i risultati:

SongName
Ricominciare
Niente è uguale

Tieni presente che, poiché il risultato è un campione, potrebbe variare ogni volta che viene eseguita la query, anche se la query è la stessa.

Questo è il piano di esecuzione:

operatore di campionamento di Bernoulli

In questo piano di esecuzione, l'operatore Random Id Assign riceve l'input da un operatore distributed union, che riceve l'input da una scansione dell'indice. L'operatore restituisce le righe con ID casuali e l'operatore Filter applica quindi un predicato scalare agli ID casuali e restituisce circa il 10% delle righe.

L'esempio seguente utilizza Reservoir

campionamento con una frequenza di campionamento di 2 righe.

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

Ecco i risultati:

SongName
I Knew You Were Magic
La seconda volta

Tieni presente che, poiché il risultato è un campione, potrebbe variare ogni volta che viene eseguita la query, anche se la query è la stessa.

Questo è il piano di esecuzione:

operatore di campionamento del serbatoio

In questo piano di esecuzione, l'operatore Random Id Assign riceve l'input da un operatore distributed union, che riceve l'input da una scansione dell'indice. L'operatore restituisce le righe con ID casuali e l'operatore Sort applica l'ordinamento agli ID casuali e applica LIMIT con 2 righe.

Sindacato locale

Un operatore unione suddivisione locale trova le suddivisioni della tabella archiviate sul server locale, esegue una sottoquery su ogni suddivisione e poi crea un'unione che combina tutti i risultati.

Una unione di suddivisione locale viene visualizzata nei piani di esecuzione che analizzano una tabella di posizionamento. I posizionamenti possono aumentare il numero di suddivisioni in una tabella, rendendo più efficiente la scansione delle suddivisioni in batch in base alle loro posizioni di archiviazione fisica.

Ad esempio, supponiamo che la tabella Singers utilizzi una chiave di posizionamento per partizionare i dati dei cantanti:

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

Ora considera questa query:

SELECT BirthDate FROM Singers;

Questo è il piano di esecuzione:

operatore di unione split locale

L'unione distribuita invia una sottoquery a ogni batch di divisioni memorizzate fisicamente insieme nello stesso server. Su ogni server, l'unione delle suddivisioni locali trova le suddivisioni che archiviano i dati Singers, esegue la sottoquery su ogni suddivisione e restituisce i risultati combinati. In questo modo, l'unione distribuita e l'unione locale suddivisa funzionano insieme per eseguire la scansione in modo efficiente della tabella Singers. Senza un'operazione Union suddivisa locale, l'operazione Union distribuita invierebbe una RPC per suddivisione anziché per batch di suddivisioni, con conseguenti round trip RPC ridondanti quando c'è più di una suddivisione per batch.

RowToDataBlockAdapter

Un operatore RowToDataBlockAdapter viene inserito automaticamente dall'ottimizzatore di query Spanner tra una coppia di operatori che operano utilizzando metodi di esecuzione diversi. Il suo input è un operatore che utilizza il metodo di esecuzione orientato alle righe e il suo output viene inserito in un operatore che esegue il metodo di esecuzione orientato ai batch. Per saperne di più, consulta Ottimizzare l'esecuzione delle query.

Serializza risultato

Un operatore serialize result è un caso speciale dell'operatore compute struct che serializza ogni riga del risultato finale della query per restituirla al client.

Ad esempio, utilizzando questa query:

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

La query richiede un array di SongName e SongGenre in base a SingerId.

Ecco i risultati:

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

Questo è il piano di esecuzione:

Operatore di serializzazione dei risultati

L'operatore di serializzazione dei risultati crea un risultato che contiene, per ogni riga della tabella Singers, un array di coppie SongName e SongGenre per i brani del cantante.

Ordina

Un operatore sort legge le righe di input, le ordina per colonna o colonne e poi restituisce i risultati ordinati.

Ad esempio, utilizzando questa query:

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

Ecco i risultati:

SongGenre
BLUES
BLUES
BLUES
BLUES
CLASSICA
PAESE
ROCK
ROCK
ROCK

Questo è il piano di esecuzione:

operatore di ordinamento

In questo piano di esecuzione, l'operatore di ordinamento riceve le righe di input da un operatore distributed union, le ordina e le restituisce a un operatore serialize result.

Per limitare il numero di righe restituite, un operatore di ordinamento può facoltativamente avere i parametri LIMIT e OFFSET. Per gli scenari distribuiti, un operatore di ordinamento con un operatore LIMIT o OFFSET viene separato in una coppia locale-globale. Ogni server remoto applica l'ordinamento e il limite o l'offset locale alle proprie righe di input, quindi restituisce i risultati al server radice. Il server radice aggrega le righe inviate dai server remoti, le ordina e poi applica il limite/offset globale.

Ad esempio, utilizzando questa query:

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

Ecco i risultati:

SongGenre
BLUES
BLUES
BLUES

Questo è il piano di esecuzione:

operatore di ordinamento con limite

Il piano di esecuzione mostra il limite locale per i server remoti e il limite globale per il server radice.

TVF

Un operatore funzione con valori di tabella produce l'output leggendo le righe di input e applicando la funzione specificata. La funzione potrebbe implementare la mappatura e restituire lo stesso numero di righe dell'input. Può anche essere un generatore che restituisce più righe o un filtro che restituisce meno righe.

Ad esempio, utilizzando questa query:

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

Ecco i risultati:

Genere SongName
Paese Not About The Guitar
Rock La seconda volta
Pop Ricominciare
Pop Niente è uguale
Paese Torniamo insieme
Pop I Knew You Were Magic
Elettronica Blu
Rock 42
Rock Fight Story

Questo è il piano di esecuzione:

operatore tvf

Input dell'unione

Un operatore Union Input restituisce i risultati a un operatore Union All. Consulta l'operatore Union All per un esempio di operatore di input Union in un piano di esecuzione.

Operatori binari

Un operatore binario è un operatore che ha due figli relazionali. I seguenti operatori sono operatori binari:

Cross apply

Un operatore Cross Apply esegue una query di tabella su ogni riga recuperata da una query di un'altra tabella e restituisce l'unione di tutte le esecuzioni di query di tabella. Gli operatori Cross Apply e Outer Apply eseguono l'elaborazione orientata alle righe, a differenza degli operatori che eseguono l'elaborazione basata su set, come Hash Join . L'operatore Cross Apply ha due input, input e map. L'operatore Cross Apply applica ogni riga del lato input al lato mappa. Il risultato dell'applicazione incrociata ha colonne sia dal lato dell'input che da quello della mappa.

Ad esempio, utilizzando questa query:

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

La query chiede il nome di ogni cantante, insieme al nome di una sola delle sue canzoni.

Ecco i risultati:

Nome Non specificato
Alice Not About The Guitar
Catalina Torniamo insieme
Davide NULL
Lea NULL
Marc NULL

La prima colonna viene compilata dalla tabella Singers, mentre la seconda dalla tabella Songs. Nei casi in cui esisteva un SingerId nella tabella Singers ma non c'era un SingerId corrispondente nella tabella Songs, la seconda colonna contiene NULL.

Questo è il piano di esecuzione:

operatore cross apply

Il nodo di primo livello è un operatore distributed union. L'operatore distributed union distribuisce i piani secondari ai server remoti. Il piano secondario contiene un operatore serialize result che calcola il nome del cantante e il nome di una delle sue canzoni e serializza ogni riga dell'output.

L'operatore di serializzazione dei risultati riceve l'input da un operatore di applicazione incrociata. Il lato di input dell'operatore Cross Apply è una scansione della tabella Singers.

Il lato della mappa per l'operazione di applicazione incrociata contiene quanto segue (dall'alto verso il basso):

  • Un operatore aggregato che restituisce Songs.SongName.
  • Un operatore limit che limita il numero di brani restituiti a uno per cantante.
  • Una scansione dell'indice sull'indice SongsBySingerAlbumSongNameDesc.

L'operatore Cross Apply mappa ogni riga del lato input a una riga del lato mappa che ha lo stesso SingerId. L'output dell'operatore cross apply è il valore FirstName della riga di input e il valore SongName della riga della mappa. (Il valore di SongName è NULL se non esiste una riga della mappa che corrisponda a SingerId.) L'operatore Union distribuito nella parte superiore del piano di esecuzione combina tutte le righe di output dei server remoti e le restituisce come risultati della query.

Hash join

Un operatore hash join è un'implementazione basata su hash dei join SQL. I join hash eseguono l'elaborazione basata su set. L'operatore di hash join legge le righe dell'input contrassegnate come build e le inserisce in una tabella hash in base a una condizione di join. L'operatore di hash join legge quindi le righe dell'input contrassegnato come probe. Per ogni riga letta dall'input della sonda, l'operatore di hash join cerca le righe corrispondenti nella tabella hash. L'operatore di hash join restituisce le righe corrispondenti come risultato.

Ad esempio, utilizzando questa query:

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;

Ecco i risultati:

AlbumTitle SongName
Niente a che fare con me Not About The Guitar
Verde La seconda volta
Verde Ricominciare
Verde Niente è uguale
Verde Torniamo insieme
Verde I Knew You Were Magic
Verde Blu
Verde 42
Terrorizzato Fight Story

Questo è il piano di esecuzione:

operatore di hash join

Nel piano di esecuzione, build è un'unione distribuita che distribuisce le scansioni nella tabella Albums. Probe è un operatore di unione distribuita che distribuisce le scansioni sull'indice SongsBySingerAlbumSongNameDesc. L'operatore Hash Join legge tutte le righe dal lato di build. Ogni riga di build viene inserita in una tabella hash in base alle colonne della condizione a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId. Successivamente, l'operatore hash join legge tutte le righe del lato probe. Per ogni riga di probing, l'operatore di hash join cerca corrispondenze nella tabella hash. Le corrispondenze risultanti vengono restituite dall'operatore hash join.

Le corrispondenze risultanti nella tabella hash potrebbero anche essere filtrate in base a una condizione residua prima di essere restituite. Un esempio di dove compaiono le condizioni residue è nei join di non uguaglianza. I piani di esecuzione di Hash Join possono essere complessi a causa della gestione della memoria e delle varianti di join. L'algoritmo principale di hash join è adattato per gestire le varianti di inner join, semi join, anti join e outer join.

Unione

Un operatore merge join è un'implementazione basata sull'unione del join SQL. Entrambe le parti del join producono righe ordinate in base alle colonne utilizzate nella condizione di join. Il merge join utilizza entrambi i flussi di input contemporaneamente e restituisce le righe quando la condizione di join è soddisfatta. Se gli input non sono originariamente ordinati come richiesto, l'ottimizzatore aggiunge operatori Sort espliciti al piano.

Unione di tipo Merge non viene selezionata automaticamente dall'ottimizzatore. Per utilizzare questo operatore, imposta il metodo di unione su MERGE_JOIN nel suggerimento per la query, come mostrato nell'esempio seguente:

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;

Ecco i risultati:

AlbumTitle SongName
Verde La seconda volta
Verde Ricominciare
Verde Niente è uguale
Verde Torniamo insieme
Verde I Knew You Were Magic
Verde Blu
Verde 42
Terrorizzato Fight Story
Niente a che fare con me Not About The Guitar

Questo è il piano di esecuzione:

merge join operator_1

In questo piano di esecuzione, l'unione di tipo merge viene distribuita in modo che l'unione venga eseguita dove si trovano i dati. Ciò consente inoltre al merge join in questo esempio di operare senza l'introduzione di operatori di ordinamento aggiuntivi, perché entrambe le scansioni delle tabelle sono già ordinate per SingerId, AlbumId, che è la condizione di join. In questo piano, la scansione sul lato sinistro della tabella Albums avanza ogni volta che la coppia SingerId, AlbumId è relativamente inferiore alla coppia SingerId_1, AlbumId_1 dell'indice di scansione SongsBySingerAlbumSongNameDesc sul lato destro. Allo stesso modo, il lato destro avanza quando è inferiore al lato sinistro. Questa unione continua a cercare equivalenze in modo che possano essere restituite le corrispondenze risultanti.

Considera un altro esempio di unione di tipo merge utilizzando la seguente query:

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

Produce i seguenti risultati:

AlbumTitle SongName
Total Junk La seconda volta
Total Junk Ricominciare
Total Junk Niente è uguale
Total Junk Torniamo insieme
Total Junk I Knew You Were Magic
Total Junk Blu
Total Junk 42
Total Junk Not About The Guitar
Verde La seconda volta
Verde Ricominciare
Verde Niente è uguale
Verde Torniamo insieme
Verde I Knew You Were Magic
Verde Blu
Verde 42
Verde Not About The Guitar
Niente a che fare con me La seconda volta
Niente a che fare con me Ricominciare
Niente a che fare con me Niente è uguale
Niente a che fare con me Torniamo insieme
Niente a che fare con me I Knew You Were Magic
Niente a che fare con me Blu
Niente a che fare con me 42
Niente a che fare con me Not About The Guitar
Riproduci La seconda volta
Riproduci Ricominciare
Riproduci Niente è uguale
Riproduci Torniamo insieme
Riproduci I Knew You Were Magic
Riproduci Blu
Riproduci 42
Riproduci Not About The Guitar
Terrorizzato Fight Story

Questo è il piano di esecuzione:

merge join operator_2

Nel piano di esecuzione precedente, l'ottimizzatore delle query ha introdotto operatori Sort aggiuntivi per ottenere le proprietà necessarie per l'esecuzione del merge join. La condizione JOIN nella query di questo esempio riguarda solo AlbumId, che non è il modo in cui vengono archiviati i dati, quindi è necessario aggiungere un ordinamento. Il motore di query supporta un algoritmo di unione distribuita, che consente l'ordinamento in locale anziché a livello globale, il che distribuisce e parallelizza il costo della CPU.

Le corrispondenze risultanti potrebbero anche essere filtrate in base a una condizione residua prima di essere restituite. Un esempio di dove vengono visualizzate le condizioni residue è nei join di non uguaglianza. I piani di esecuzione di unione possono essere complessi a causa di ulteriori requisiti di ordinamento. L'algoritmo principale di unione è adattato per gestire le varianti di inner join, semi join, anti join e outer join.

Push broadcast hash join

Un operatore push broadcast hash join è un'implementazione distribuita basata su hash join di join SQL. L'operatore di join hash push broadcast legge le righe dal lato di input per costruire un batch di dati. Questo batch viene poi trasmesso a tutti i server contenenti dati laterali della mappa. Sui server di destinazione in cui viene ricevuto il batch di dati, viene creata un'unione hash utilizzando il batch come dati del lato di creazione e i dati locali vengono poi analizzati come lato di probing dell'unione hash.

Push broadcast hash join non viene selezionato automaticamente dall'ottimizzatore. Per utilizzare questo operatore, imposta il metodo di unione su PUSH_BROADCAST_HASH_JOIN nel suggerimento per la query, come mostrato nell'esempio seguente:

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;

Ecco i risultati:

AlbumTitle SongName
Verde La seconda volta
Verde Ricominciare
Verde Niente è uguale
Verde Torniamo insieme
Verde I Knew You Were Magic
Verde Blu
Verde 42
Terrorizzato Fight Story
Niente a che fare con me Not About The Guitar

Questo è il piano di esecuzione:

operatore push_broadcast hash_join

L'input dell'unione hash della trasmissione push è l'indice AlbumsByAlbumTitle. L'input viene serializzato in un batch di dati. Il batch viene quindi inviato a tutte le suddivisioni locali dell'indice SongsBySingerAlbumSongNameDesc, dove viene deserializzato e creato in una tabella hash. La tabella hash utilizza quindi i dati dell'indice locale come probe che restituisce le corrispondenze risultanti.

Le corrispondenze risultanti potrebbero anche essere filtrate in base a una condizione residua prima di essere restituite. Un esempio di dove vengono visualizzate le condizioni residue è nei join di non uguaglianza.

Applica esterno

Un operatore Outer Apply è simile a un operatore Cross Apply, solo che un operatore Outer Apply garantisce che ogni esecuzione sul lato mappa restituisca almeno una riga producendo una riga di valori NULL se necessario. (In altre parole, fornisce la semantica del left outer join.)

Unione ricorsiva

Un operatore recursive union esegue l'unione di due input, uno che rappresenta un caso base e l'altro che rappresenta un caso recursive. Viene utilizzato nelle query grafiche con attraversamenti di percorsi quantificati. L'input di base viene elaborato per primo e una sola volta. L'input ricorsivo viene elaborato finché la ricorsione non termina. La ricorsione termina quando viene raggiunto il limite superiore, se specificato, o quando la ricorsione non produce nuovi risultati. Nell'esempio seguente, la tabella Collaborations viene aggiunta allo schema e viene creato un grafico delle proprietà denominato 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),
);

La seguente query del grafico trova i cantanti che hanno collaborato con un determinato cantante o con i suoi collaboratori.

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

operatore di unione ricorsiva

L'operatore di unione ricorsiva filtra la tabella Singers per trovare il cantante con il SingerId specificato. Questo è l'input di base per l'unione ricorsiva. L'input ricorsivo per l'unione ricorsiva comprende un cross-apply distribuito o un altro operatore di join per altre query che uniscono ripetutamente la tabella Collaborations con i risultati dell'iterazione precedente del join. Le righe del modulo di input di base costituiscono la prima iterazione. A ogni iterazione, l'output viene archiviato dalla scansione dello spool ricorsivo. Le righe della scansione spool ricorsiva vengono unite alla tabella Collaborations in spoolscan.featuredSingerId = Collaborations.SingerId. La ricorsione termina al completamento di due iterazioni, poiché questo è il limite superiore specificato nella query.

Operatori n-ari

Un operatore N-ario è un operatore che ha più di due elementi secondari relazionali. I seguenti operatori sono operatori N-ari:

Union all

Un operatore Union All combina tutti i set di righe dei relativi elementi secondari senza rimuovere i duplicati. Gli operatori Union All ricevono l'input dagli operatori Union Input distribuiti su più server. L'operatore Union All richiede che gli input abbiano lo stesso schema, ovvero lo stesso insieme di tipi di dati per ogni colonna.

Ad esempio, utilizzando questa query:

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

Il tipo di riga per i figli è costituito da due numeri interi.

Ecco i risultati:

a b
1 2
3 4
5 6

Questo è il piano di esecuzione:

union_all_operator

L'operatore Union All combina le righe di input e, in questo esempio, invia i risultati a un operatore Serialize Result.

Una query come la seguente andrebbe a buon fine, perché per ogni colonna viene utilizzato lo stesso insieme di tipi di dati, anche se i figli utilizzano variabili diverse per i nomi delle colonne:

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

Una query come la seguente non andrebbe a buon fine perché i figli utilizzano tipi di dati diversi per le colonne:

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

Sottoquery scalari

Una sottoquery scalare è una sottoespressione SQL che fa parte di un'espressione scalare. Spanner tenta di rimuovere le sottoquery scalari laddove possibile. In alcuni scenari, tuttavia, i piani possono contenere esplicitamente sottoquery scalari.

Ad esempio, utilizzando questa query:

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

Questa è la sottoespressione SQL:

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

Ecco i risultati (della query completa):

Nome
Alice 1
Catalina 0
Davide 0
Lea 0
Marc 0

Questo è il piano di esecuzione:

operatore di sottoquery scalare

Il piano di esecuzione contiene una sottoquery scalare, mostrata come Scalar Subquery, su un operatore aggregate.

A volte Spanner converte le sottoquery scalari in un altro operatore, ad esempio un join o un cross apply, per migliorare potenzialmente le prestazioni.

Ad esempio, utilizzando questa query:

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

Questa è la sottoespressione SQL:

SELECT MAX(Duration)
FROM Songs;

Ecco i risultati (della query completa):

SingerId AlbumId TrackId SongName Durata SongGenre
2 1 6 Niente è uguale 303 BLUES

Questo è il piano di esecuzione:

L&#39;operatore di sottoquery scalare non viene visualizzato nel piano

Il piano di esecuzione non contiene una sottoquery scalare perché Spanner ha convertito la sottoquery scalare in un'applicazione incrociata.

Sottoquery di array

Una sottoquery array è simile a una sottoquery scalare, tranne per il fatto che la sottoquery può utilizzare più di una riga di input. Le righe utilizzate vengono convertite in un unico array di output scalare contenente un elemento per riga di input utilizzata.

Ad esempio, utilizzando questa query:

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

Questa è la sottoquery:

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

I risultati della sottoquery per ogni AlbumId vengono convertiti in un array di righe ConcertDate rispetto a quel AlbumId. Il piano di esecuzione contiene una sottoquery di array, mostrata come Array Subquery, sopra un operatore di unione distribuita:

operatore di sottoquery dell&#39;array

Operatori distribuiti

Gli operatori descritti in precedenza in questa pagina vengono eseguiti entro i limiti di un singolo computer. Gli operatori distribuiti vengono eseguiti su più server.

I seguenti operatori sono operatori distribuiti:

L'operatore Distributed Union è l'operatore primitivo da cui derivano Distributed Cross Apply e Distributed Outer Apply.

Gli operatori distribuiti vengono visualizzati nei piani di esecuzione con una variante di unione distribuita sopra una o più varianti di unione distribuita locale. Una variante di un'operazione Union distribuita esegue la distribuzione remota dei sottopiani. Una variante dell'unione distribuita locale si trova in cima a ciascuna delle scansioni eseguite per la query, come mostrato in questo piano di esecuzione:

operatore distribuito

Le varianti dell'operazione Union distribuita locale garantiscono l'esecuzione stabile delle query quando si verificano riavvii perconfini della suddivisionee che cambiano dinamicamente.

Quando possibile, una variante di unione distribuita ha un predicato di suddivisione che comporta l'eliminazione delle suddivisioni, il che significa che i server remoti eseguono i sottopiani solo sulle suddivisioni che soddisfano il predicato. Ciò migliora sia la latenza che il rendimento complessivo delle query.

Distributed union

Un operatore distributed union divide concettualmente una o più tabelle in più split, valuta in remoto una sottoquery in modo indipendente su ogni split e poi unisce tutti i risultati.

Ad esempio, utilizzando questa query:

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

Ecco i risultati:

SongName SongGenre
Ricominciare ROCK
La seconda volta ROCK
Fight Story ROCK

Questo è il piano di esecuzione:

operatore di unione distribuita

L'operatore di unione distribuita invia i piani secondari ai server remoti, che eseguono una scansione della tabella tra le suddivisioni che soddisfano il predicato WHERE s.SingerId = 2 AND s.SongGenre = 'ROCK' della query. Un operatore serialize result calcola i valori SongName e SongGenre dalle righe restituite dalle scansioni delle tabelle. L'operatore di unione distribuita restituisce quindi i risultati combinati dei server remoti come risultati della query SQL.

Unione di unione distribuita

L'operatore distributed merge union distribuisce una query su più server remoti. Quindi combina i risultati della query per produrre un risultato ordinato, noto come ordinamento di unione distribuito.

Un'unione di merge distribuita esegue i seguenti passaggi:

  1. Il server radice invia una sottoquery a ogni server remoto che ospita una divisione dei dati sottoposti a query. La sottoquery include istruzioni che i risultati vengono ordinati in un ordine specifico.

  2. Ogni server remoto esegue la sottoquery sulla propria suddivisione, quindi invia i risultati nell'ordine richiesto.

  3. Il server radice unisce la sottoquery ordinata per produrre un risultato completamente ordinato.

L'unione distribuita è attiva per impostazione predefinita per Spanner versione 3 e successive.

Distributed Cross Apply

Un operatore distributed cross apply (DCA) estende l'operatore cross apply eseguendo l'operazione su più server. I gruppi laterali di input DCA elaborano batch di righe (a differenza di un normale operatore Cross Apply, che agisce su una sola riga di input alla volta). Il lato mappa DCA è un insieme di operatori Cross Apply che vengono eseguiti su server remoti.

Ad esempio, utilizzando questa query:

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

I risultati sono nel formato:

AlbumTitle
Verde
Niente a che fare con me
Riproduci
Total Junk
Verde

Questo è il piano di esecuzione:

operatore Distributed Cross Apply

L'input DCA contiene una scansione dell'indice SongsBySingerAlbumSongNameDesc che raggruppa le righe di AlbumId. Il lato della mappa per questo operatore di applicazione incrociata è una scansione dell'indice AlbumsByAlbumTitle, soggetta al predicato di AlbumId nella riga di input che corrisponde alla chiave AlbumId nell'indice AlbumsByAlbumTitle. Il mapping restituisce SongName per i valori SingerId nelle righe di input batch.

Per riassumere la procedura DCA per questo esempio, l'input del DCA sono le righe in batch della tabella Albums e l'output del DCA è l'applicazione di queste righe alla mappa della scansione dell'indice.

Applicazione esterna distribuita

Un operatore Distributed Outer Apply estende l'operatore Outer Apply eseguendo l'operazione su più server, in modo simile a come un operatore Distributed Cross Apply estende un operatore Cross Apply.

Ad esempio, utilizzando questa query:

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

I risultati sono nel formato:

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

Questo è il piano di esecuzione:

operatore Distributed Outer Apply

Applica mutazioni

Un operatore apply mutations applica le mutazioni di un'istruzione DML (Data Manipulation Statement) alla tabella. È l'operatore principale in un piano di query per un'istruzione DML.

Ad esempio, utilizzando questa query:

DELETE FROM singers
WHERE  firstname = 'Alice';

Ecco i risultati:

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

Questo è il piano di esecuzione:

operatore apply mutations

Informazioni aggiuntive

Questa sezione descrive gli elementi che non sono operatori autonomi, ma che eseguono attività per supportare uno o più degli operatori elencati in precedenza. Gli elementi descritti qui sono tecnicamente operatori, ma non sono operatori separati nel piano di query.

Costruttore di struct

Un costruttore di struct crea uno struct, ovvero una raccolta di campi. In genere crea una struct per le righe risultanti da un'operazione di calcolo. Un costruttore di struct non è un operatore autonomo. Viene invece visualizzato negli operatori compute struct o serialize result.

Per un'operazione di struttura di calcolo, il costruttore di strutture crea una struttura in modo che le colonne per le righe calcolate possano utilizzare un unico riferimento di variabile alla struttura.

Per un'operazione di serializzazione dei risultati, il costruttore di struct crea uno struct per serializzare i risultati.

Ad esempio, utilizzando questa query:

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

Ecco i risultati:

A
1

Questo è il piano di esecuzione:

costruttore di struct

Nel piano di esecuzione, i costruttori di struct vengono visualizzati all'interno di un operatore di risultato di serializzazione.