Operatori di esecuzione delle query

Introduzione

In questa pagina vengono descritti i dettagli degli operatori utilizzati nei piani di esecuzione delle query di Spanner. Per scoprire come recuperare un piano di esecuzione per una query specifica mediante 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 di 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 leaf è un operatore che non ha elementi figlio. I tipi di operatori foglia sono:

Annullamento nidificazione array

Un operatore array unnest consente di appiattire un array di input in righe di elementi. Ogni riga risultante contiene fino a 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 unnest dell&#39;array

Genera relazione

Un operatore genera relazione restituisce zero o più righe.

Relazione unitaria

La relazione unità restituisce una riga. È 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 tra unità

Relazione vuota

La relazione vuota non restituisce righe. È un caso speciale dell'operatore generate relation.

Ad esempio, utilizzando questa query:

SELECT * FROM Albums LIMIT 0

Il risultato è:

No results

Questo è il piano di esecuzione:

operatore di relazione vuoto

Scansione

Un operatore scan restituisce righe analizzando l'origine di righe. Di seguito sono riportati i tipi di operatori di scansione:

  • Scansione della tabella: l'analisi viene eseguita su una tabella.
  • Scansione indice: la scansione viene eseguita su un indice.
  • Scansione batch: l'analisi viene eseguita su tabelle intermedie create da altri operatori relazionali (ad esempio, una tabella creata da un'applicazione incrociata distribuita).

Quando possibile, Spanner applica semplici predicati alle chiavi come parte 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'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:

LastName
Smith

Questo è il piano di esecuzione:

operatore di scansione

Nel piano di esecuzione, l'operatore unione distribuita di primo livello invia i sottopiani ai server remoti. Ogni sottopiano ha un operatore di serializza il risultato e un operatore di scansione dell'indice. Il predicato Key Predicate: FirstName = 'Catalina' limita la scansione alle righe nell'indice SingersByFirstLastname con FirstName uguale a Catalina. L'output della scansione dell'indice viene restituito all'operatore del risultato di serializzazione.

Operatori unari

Un operatore unario è un operatore con un singolo elemento figlio relazionale.

I seguenti operatori sono operatori unari:

Aggregazione

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

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 count
3 278 1
2 225,875 8

Questo è il piano di esecuzione:

operatore aggregato

Gli operatori aggregati possono essere basati su flussi o basati su hash. Il piano di esecuzione sopra riportato mostra un aggregato basato su flussi. I dati aggregati basati su flussi leggono da input già preordinati (se è presente GROUP BY) e calcolano i gruppi senza bloccare. I dati aggregati basati su hash creano tabelle hash per mantenere contemporaneamente aggregati incrementali di più righe di input. I dati aggregati basati su flussi sono più veloci e utilizzano meno memoria rispetto ai dati aggregati basati su hash, ma richiedono che l'input venga ordinato (per colonne chiave o indici secondari).

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

Applicare le mutazioni

Un operatore apply mutations applica le mutazioni da 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 applica mutazioni

Creazione batch

Un operatore create batch raggruppa le righe di input in una sequenza. Un'operazione di creazione batch in genere fa parte di un'operazione di applicazione incrociata distribuita. Le righe di input possono essere riordinate durante la creazione in batch. Il numero di righe di input raggruppate in ogni esecuzione dell'operatore batch è variabile.

Consulta l'operatore di applicazione incrociata distribuita per un esempio di operatore batch di creazione in un piano di esecuzione.

Computing

Un operatore compute produce output leggendo le righe di input e aggiungendo una o più colonne aggiuntive calcolate tramite espressioni scalari. Consulta l'operatore union all per un esempio di operatore di computing in un piano di esecuzione.

struct di computing

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:

FirstName Non specificato
Alice [["Non sulla chitarra","BLUES"]]

Questo è il piano di esecuzione:

operatore struct compute

Nel piano di esecuzione, l'operatore di sottoquery dell'array riceve input da un operatore Distributed Union, che riceve input da un operatore struct Compute. L'operatore dello struct computing crea una struttura dalle colonne SongName e SongGenre nella tabella Songs.

Filtro

Un operatore filter legge tutte le righe dal suo input, applica un predicato scalare su ogni riga, quindi 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:

LastName
Riccardo

Questo è il piano di esecuzione:

operatore di filtro

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

Per migliorare le prestazioni, 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 un'analisi della tabella cercherà le righe che corrispondono a k = 5, senza leggere l'intero input. Ciò si traduce in 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%'.

Filtra ricerca

Un operatore di analisi dei filtri è sempre sopra una scansione dell'indice o della tabella. Utilizza 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 dei filtri in determinate condizioni:

  • Condizione cercabile: questa condizione si applica se Spanner può determinare una riga specifica a cui accedere nella tabella. In genere questo accade quando il filtro si trova su un prefisso della chiave primaria. Ad esempio, se la chiave primaria è costituita da Col1 e Col2, è possibile cercare una clausola WHERE che include valori espliciti per Col1 o Col1 e Col2. In questo caso, Spanner legge solo i dati 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:

LastName
Riccardo

Questo è il piano di esecuzione:

operatore di scansione del filtro

Limite

Un operatore limit vincola il numero di righe restituite. Un parametro OFFSET facoltativo specifica la riga iniziale da restituire. Per gli scenari distribuiti, un operatore di limite può essere separato in una coppia locale/globale. Ogni server remoto applica il limite locale per le 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
Non riguarda la chitarra
La seconda volta
Ricomincia

Questo è il piano di esecuzione:

operatore limite

Il limite locale è il limite per ciascun server remoto. Il server radice aggrega le righe dei server remoti e quindi applica il limite globale.

Assegnazione ID casuale

Un operatore random idAssign produce l'output leggendo le righe di input e aggiungendo un numero casuale a ogni riga. Utilizza un operatore Filter o Sort per ottenere i 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
Ricomincia
Niente è uguale

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

Questo è il piano di esecuzione:

operatore di esempio bernoulli

In questo piano di esecuzione, l'operatore Random Id Assign riceve l'input da un operatore di 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 il campionamento Reservoir con una frequenza di campionamento di 2 righe.

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

Ecco i risultati:

SongName
Sapevo che eri magia
La seconda volta

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

Questo è il piano di esecuzione:

operatore di esempio reservoir

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

Serializza risultato

Un operatore serialize result è un caso speciale dell'operatore struct compute che serializza ogni riga del risultato finale della query, per tornare 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
[]
[[Torniamo insieme, COUNTRY], [Ricominciamo, ROCK]]
[[Non sulla chitarra, BLUES]]
[]
[]

Questo è il piano di esecuzione:

Operatore del risultato serializza

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

Ordinamento

Un operatore sort legge le righe di input, le ordina per colonne, quindi restituisce i risultati ordinati.

Ad esempio, utilizzando questa query:

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

Ecco i risultati:

SongGenre
BLU
BLU
BLU
BLU
CLASSICHE
PAESE
ROCCIA
ROCCIA
ROCCIA

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 di unione distribuita, ordina le righe di input e restituisce le righe ordinate a un operatore di serializza il risultato.

Per limitare il numero di righe restituite, un operatore di ordinamento può avere facoltativamente i parametri LIMIT e OFFSET. Per scenari distribuiti, un operatore di ordinamento con un operatore LIMIT e/o OFFSET è separato in una coppia locale/globale. Ogni server remoto applica l'ordinamento e il limite/offset locale per le 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
BLU
BLU
BLU

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.

Fascia d'età

Un operatore di funzione con valore di tabella produce l'output leggendo le righe di input e applicando la funzione specificata. La funzione può 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 Non riguarda la chitarra
Roccia La seconda volta
Pop Ricomincia
Pop Niente è uguale
Paese Ritorniamo insieme
Pop Sapevo che eri magia
Elettronica Blu
Roccia 42
Roccia Storia di un combattimento

Questo è il piano di esecuzione:

operatore tvf

Input 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 con due elementi figlio relazionali. I seguenti operatori sono operatori binari:

Applicazione incrociata

Un operatore di tipo cross apply esegue una query di tabella su ogni riga recuperata da una query su un'altra tabella e restituisce l'unione di tutte le esecuzioni della query in una 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 di applicazione incrociata ha due input, input e map. L'operatore di applicazione incrociata applica ogni riga nel lato di input al lato della mappa. Il risultato dell'applicazione incrociata include colonne sia sul lato di input che su 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 solo una delle canzoni del cantante.

Ecco i risultati:

FirstName Non specificato
Alice Non riguarda la chitarra
Catalina Ritorniamo insieme
Davide NULL
Elena NULL
Marco NULL

La prima colonna viene compilata dalla tabella Singers e la seconda dalla tabella Songs. Nei casi in cui SingerId esisteva nella tabella Singers, ma non è stato trovato SingerId 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 di Distributed Union distribuisce i sottopiani ai server remoti. Il sottopiano contiene un operatore serialize result che calcola il nome del cantante e il nome di una delle canzoni del cantante e serializza ogni riga dell'output.

L'operatore del risultato di serializzazione riceve l'input da un operatore di applicazione incrociata. Il lato di input per l'operatore di applicazione incrociata è una scansione della tabella nella tabella Singers.

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

  • Un operatore aggregate 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 di applicazione incrociata mappa ogni riga dal lato di input a una riga nella mappa che ha lo stesso SingerId. L'output dell'operatore di applicazione incrociata è il valore FirstName della riga di input e il valore SongName della riga della mappa. Il valore SongName sarà NULL se non esiste una riga della mappa corrispondente a SingerId. L'operatore di unione distribuita nella parte superiore del piano di esecuzione combina quindi tutte le righe di output dai server remoti e le restituisce come risultati della query.

Unisci hash

Un operatore hash join è un'implementazione basata su hash dei join SQL. Un join di hash esegue l'elaborazione basata su set. L'operatore di hash join legge le righe dall'input contrassegnato come build e le inserisce in una tabella hash in base a una condizione di join. L'operatore di join hash legge quindi le righe dall'input contrassegnato come probe. Per ogni riga che legge dall'input del probe, l'operatore di join hash cerca le righe corrispondenti nella tabella hash. L'operatore di join hash 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 da fare con me Non riguarda la chitarra
Verde La seconda volta
Verde Ricomincia
Verde Niente è uguale
Verde Ritorniamo insieme
Verde Sapevo che eri magia
Verde Blu
Verde 42
Terrorizzato Storia di un combattimento

Questo è il piano di esecuzione:

Operatore di join hash

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

Le corrispondenze risultanti nella tabella hash possono anche essere filtrate in base a una condizione residua prima di essere restituite. (Un esempio di condizione in cui appaiono condizioni residue è nei join non di uguaglianza). I piani di esecuzione 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 inner, semi, anti e outer join.

Unisci e unisci

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

L'opzione Unisci join non viene selezionata automaticamente dall'ottimizzatore. Per utilizzare questo operatore, imposta il metodo di join 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 Ricomincia
Verde Niente è uguale
Verde Ritorniamo insieme
Verde Sapevo che eri magia
Verde Blu
Verde 42
Terrorizzato Storia di un combattimento
Niente da fare con me Non riguarda la chitarra

Questo è il piano di esecuzione:

Operatore di join di unione_1

In questo piano di esecuzione, il join viene distribuito in modo che venga eseguito nella posizione in cui si trovano i dati. Ciò consente anche al join di unione in questo esempio di operare senza l'introduzione di operatori di ordinamento aggiuntivi, poiché entrambe le analisi delle tabelle sono già ordinate per SingerId, AlbumId, che è la condizione di join. In questo piano, la scansione del lato sinistro della tabella Albums avanza ogni volta che la sua SingerId, AlbumId è relativamente inferiore alla scansione dell'indice SongsBySingerAlbumSongNameDesc sul lato destro SingerId_1, AlbumId_1 coppia. Analogamente, il lato destro avanza ogni volta che è inferiore a quello sinistro. L'avanzamento dell'unione continua a cercare equivalenze in modo da restituire le corrispondenze risultanti.

Considera un altro esempio di unione di unione 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;

Questo genera i seguenti risultati:

AlbumTitle SongName
Spazzatura totale La seconda volta
Spazzatura totale Ricomincia
Spazzatura totale Niente è uguale
Spazzatura totale Ritorniamo insieme
Spazzatura totale Sapevo che eri magia
Spazzatura totale Blu
Spazzatura totale 42
Spazzatura totale Non riguarda la chitarra
Verde La seconda volta
Verde Ricomincia
Verde Niente è uguale
Verde Ritorniamo insieme
Verde Sapevo che eri magia
Verde Blu
Verde 42
Verde Non riguarda la chitarra
Niente da fare con me La seconda volta
Niente da fare con me Ricomincia
Niente da fare con me Niente è uguale
Niente da fare con me Ritorniamo insieme
Niente da fare con me Sapevo che eri magia
Niente da fare con me Blu
Niente da fare con me 42
Niente da fare con me Non riguarda la chitarra
Gioca La seconda volta
Gioca Ricomincia
Gioca Niente è uguale
Gioca Ritorniamo insieme
Gioca Sapevo che eri magia
Gioca Blu
Gioca 42
Gioca Non riguarda la chitarra
Terrorizzato Storia di un combattimento

Questo è il piano di esecuzione:

Operatore di join di unione_2

Nel piano di esecuzione precedente, lo strumento di ottimizzazione delle query ha introdotto altri operatori Sort per ottenere le proprietà necessarie per l'esecuzione del join di unione. La condizione JOIN nella query di questo esempio è solo su 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 di eseguire l'ordinamento localmente anziché globalmente, che distribuisce e parallelizza il costo della CPU.

Le corrispondenze risultanti possono anche essere filtrate in base a una condizione residua prima di essere restituite. (Un esempio di condizione in cui appaiono condizioni residue è rappresentato dai join non di uguaglianza). I piani di esecuzione di Merge join possono essere complessi a causa di requisiti di ordinamento aggiuntivi. L'algoritmo principale di join di unione è adattato per gestire le varianti interno, semi, anti ed esterno del join.

Push join hash join

Un operatore di unione hash broadcast push è un'implementazione di join SQL distribuita basata su hash-join. L'operatore di join hash della trasmissione push legge le righe dal lato di input per creare un batch di dati. Il batch viene quindi trasmesso a tutti i server contenenti i dati a livello della mappa. Sui server di destinazione in cui viene ricevuto il batch di dati, viene creato un join hash utilizzando il batch come dati del lato build e i dati locali vengono quindi analizzati come lato probe del join hash.

Il join hash della trasmissione push non viene selezionato automaticamente dall'ottimizzatore. Per utilizzare questo operatore, imposta il metodo di join su PUSH_BROADCAST_HASH_JOIN nel suggerimento della 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 Ricomincia
Verde Niente è uguale
Verde Ritorniamo insieme
Verde Sapevo che eri magia
Verde Blu
Verde 42
Terrorizzato Storia di un combattimento
Niente da fare con me Non riguarda la chitarra

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 il batch viene quindi 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 possono anche essere filtrate in base a una condizione residua prima di essere restituite. (Un esempio di condizione in cui appaiono condizioni residue è nei join non di uguaglianza).

Applicazione esterna

Un operatore outer apply è simile a un operatore cross apply, tranne per il fatto che un operatore outer apply assicura che ogni esecuzione sul lato della mappa restituisca almeno una riga mediante la produzione di una riga riempita con NULL, se necessario. In altre parole, fornisce la semantica del join esterno sinistro.

Operatori n-ari

Un operatore N-ary è un operatore con più di due elementi figlio relazionali. I seguenti operatori sono operatori N-ary:

Unisci tutto

Un operatore union all combina tutti gli insiemi di righe dei relativi elementi figlio senza rimuovere i duplicati. Tutti gli operatori dell'unione ricevono il proprio input dagli operatori union input distribuiti su più server. L'operatore Union All richiede che i suoi input abbiano lo stesso schema, ovvero lo stesso set 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 gli elementi secondari è composto 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 serializza il risultato.

Una query come la seguente potrebbe avere esito positivo, perché viene utilizzato lo stesso set di tipi di dati per ogni colonna, anche se gli elementi secondari 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 potrebbe avere esito positivo, perché i publisher secondari 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 scalabile è una sottoespressione SQL che fa parte di un'espressione scalare. Spanner tenta di rimuovere le sottoquery scalari quando 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;

Questi sono i risultati (della query completa):

FirstName
Alice 1
Catalina 0
Davide 0
Elena 0
Marco 0

Questo è il piano di esecuzione:

Operatore di sottoquery scalare

Il piano di esecuzione contiene una sottoquery scalare, come Sottoquery scala, sopra un operatore aggregate.

A volte Spanner converte sottoquery scalari in un altro operatore, come join o cross apply, per migliorare 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;

Questi sono i risultati (della query completa):

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

Questo è il piano di esecuzione:

operatore di sottoquery scalare non 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 di 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 singolo 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 ConcertDate righe per quel AlbumId. Il piano di esecuzione contiene una sottoquery array, mostrata come Sottoquery array, 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 confini di una singola macchina. Gli operatori distribuiti vengono eseguiti su più server.

I seguenti operatori sono operatori distribuiti:

L'operatore Distributed Union è l'operatore primitivo da cui derivano i sistemi Distributed Cross apply e Distributed Outer apply.

Gli operatori distribuiti vengono visualizzati nei piani di esecuzione con una variante Unione distribuita sopra una o più varianti dell'Unione distribuita locale. Una variante dell'unione distribuita esegue la distribuzione remota dei sottopiani. Una variante di unione distribuita locale è sopra ogni analisi eseguita per la query, come mostrato in questo piano di esecuzione:

operatore distribuito

Le varianti di unione distribuita locale assicurano un'esecuzione stabile delle query in caso di riavvii in caso di modifica dinamica dei confini della suddivisione.

Quando possibile, una variante di un'unione distribuita ha un predicato di suddivisione che genera l'eliminazione di una suddivisione, il che significa che i server remoti eseguono i sottopiani solo sulle suddivisioni che soddisfano il predicato. Questo migliora sia la latenza che le prestazioni complessive delle query.

Unione distribuita

Un operatore Distributed Union divide concettualmente una o più tabelle in più split, valuta in remoto una sottoquery in modo indipendente su ogni suddivisione e quindi 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
Ricomincia ROCCIA
La seconda volta ROCCIA
Storia di un combattimento ROCCIA

Questo è il piano di esecuzione:

operatore Distributed Union

L'operatore di unione distribuita invia i sottopiani 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 della tabella. L'operatore di unione distribuita restituisce quindi i risultati combinati dai server remoti come risultati della query SQL.

Unione distribuita distribuita

L'operatore istituto di unione distribuito 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 unione distribuita esegue i seguenti passaggi:

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

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

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

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

Applicazione incrociata distribuita

Un operatore Distributed Cross apply (DCA) estende l'operatore cross apply eseguendo l'operazione su più server. Il lato di input DCA raggruppa batch di righe (a differenza di un normale operatore di applicazione incrociata, 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 da fare con me
Gioca
Spazzatura totale
Verde

Questo è il piano di esecuzione:

operatore Distributed Cross apply

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

Per riassumere il processo DCA per questo esempio, l'input del DCA è costituito dalle righe in batch dalla 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 di applicazione esterna eseguendolo su più server, in modo simile a come un operatore di applicazione incrociata distribuita estende un operatore di applicazione incrociata.

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:

LastName ConcertDate
Trento 2014-02-18
Smith 2011-09-03
Smith 2010-06-06
Lomando 2005-04-30
Martina 2015-11-04
Riccardo

Questo è il piano di esecuzione:

operatore Distributed Outer apply

Applicare le mutazioni

Un operatore apply mutations applica le mutazioni da 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 applica mutazioni

Informazioni aggiuntive

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

Costruttore di struct

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

Per un'operazione di calcolo di uno struct, il costruttore crea uno struct in modo che le colonne per le righe calcolate possono utilizzare un singolo riferimento di variabile allo struct.

Per un'operazione di serializzazione dei risultati, il costruttore 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 struct

Nel piano di esecuzione, i costruttori struct appaiono all'interno di un operatore dei risultati di serializzazione.