Introduzione
In questa pagina vengono descritti i dettagli sugli 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 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 secondari. I tipi di operatori foglia sono:
Annulla nidificazione dell'array
Un operatore array unnest appiattisce 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:
Genera relazione
Un operatore genera relazione restituisce zero o più righe.
Relazione unitaria
La relazione unitaria restituisce una riga. È un caso speciale dell'operatore genera relazione.
Ad esempio, utilizzando questa query:
SELECT 1 + 2 AS Result;
Il risultato è:
Risultato |
---|
3 |
Questo è il piano di esecuzione:
Relazione vuota
La relazione vuota non restituisce alcuna riga. È un caso speciale dell'operatore genera relazione.
Ad esempio, utilizzando questa query:
SELECT * FROM Albums LIMIT 0
Il risultato è:
No results
Questo è il piano di esecuzione:
Eseguire un'analisi
Un operatore di scansione restituisce le righe analizzandone un'origine. Ecco i tipi di operatori di scansione:
- Scansione della tabella: l'analisi 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' applicazione incrociata distribuita).
Quando possibile, Spanner applica predicati semplici alle chiavi come parte di una scansione. Le scansioni vengono eseguite in modo più efficiente quando vengono applicati i predicati, in quanto non ha bisogno di 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 comporta un'analisi 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:
Nel piano di esecuzione, l'operatore dell'unione distribuita di primo livello invia i sottopiani ai server remoti. Ogni sottopiano ha un operatore di serializza i risultati 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 dei risultati di serializzazione.
Operatori unari
Un operatore unario è un operatore che ha un singolo elemento figlio relazionale.
I seguenti operatori sono operatori unari:
- Aggrega
- Applicare mutazioni
- Crea batch
- Computing
- struct di calcolo
- Filtro
- Analisi dei filtri
- Limite
- Assegnazione ID casuale
- Serializza il risultato
- Ordina
- Input dell'Unione
Aggregazione
Un operatore aggregate implementa le istruzioni SQL GROUP BY
e le funzioni
aggregate (come COUNT
). L'input per un operatore aggregato viene partizionato logicamente
in gruppi organizzati su colonne chiave (o in un singolo gruppo se
GROUP BY
non è presente). Per ogni gruppo vengono calcolati zero o più dati 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 in base a SingerId
ed esegue un'aggregazione di tipo AVG
e
COUNT
.
Ecco i risultati:
SingerId | media | conteggio |
---|---|---|
3 | 278 | 1 |
2 | 225,875 | 8 |
Questo è il piano di esecuzione:
Gli operatori aggregati possono essere basati su stream o basati su hash. Il piano di esecuzione riportato sopra mostra un dato aggregato basato su stream. I dati aggregati basati su flusso leggono da input già preordinati (se è presente GROUP BY
) e calcolano i gruppi senza
bloccare. Le tabelle hash di build aggregate basate su hash per mantenere aggregati
incrementali di più righe di input contemporaneamente. I dati aggregati basati su stream sono più veloci e utilizzano meno memoria rispetto a quelli basati su hash, ma richiedono che l'input venga ordinato (per colonne chiave o per indici secondari).
Per gli scenari distribuiti, un operatore aggregato può essere separato in una coppia locale/globale. Ogni server remoto esegue l'aggregazione locale sulle righe di input e poi restituisce i risultati al server principale. Il server radice esegue l'aggregazione globale.
Applicare mutazioni
Un operatore apply mutations applica alla tabella le mutazioni di un'istruzione DML. È 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:
Creazione batch
Un operatore di creazione batch raggruppa le righe di input in una sequenza. Un'operazione di creazione batch di solito avviene come parte di un'operazione Distributed Cross apply. Le righe di input possono essere riordinate durante il raggruppamento. Il numero di righe di input che vengono raggruppate in ogni esecuzione dell'operatore batch è variabile.
Consulta l'operatore Distributed Cross apply 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 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 colonna 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 | [["Not About The Chitarra","BLUES"]] |
Questo è il piano di esecuzione:
Nel piano di esecuzione, l'operatore di sottoquery di array riceve input da un operatore di unione distribuita, che riceve input da un operatore di struct di calcolo. L'operatore di struct Compute crea una struttura dalle
colonne SongName
e SongGenre
nella tabella Songs
.
Filtro
Un operatore filter legge tutte le righe dal relativo input, applica un predicato scalare su ogni riga e poi 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 |
---|
Richards |
Questo è il piano di esecuzione:
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ò porta a 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%'
.
Analisi filtri
Un operatore di scansione dei filtri si trova sempre sopra una scansione di tabella o indice. Utilizza la scansione per ridurre il numero di righe lette dal database. L'analisi risultante è in genere più veloce rispetto a un filtro. Spanner applica l'analisi dei filtri in determinate condizioni:
- Condizione ricercabile: la condizione ricercabile si applica se Spanner può determinare una riga specifica a cui accedere nella tabella. In generale, questo accade quando il filtro si trova su un prefisso della chiave primaria. Ad esempio, se la chiave primaria è costituita da
Col1
eCol2
, è possibile cercare una clausolaWHERE
che include valori espliciti perCol1
oppureCol1
eCol2
. In questo caso, Spanner legge solo i dati che rientrano nell'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 |
---|
Richards |
Questo è il piano di esecuzione:
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, quindi applica il limite globale.
Ad esempio, utilizzando questa query:
SELECT s.SongName
FROM Songs AS s
LIMIT 3;
Ecco i risultati:
SongName |
---|
Non una chitarra |
La seconda volta |
Ricomincia |
Questo è il piano di esecuzione:
Il limite locale è il limite per ogni server remoto. Il server radice aggrega le righe dei server remoti, quindi applica il limite globale.
Assegnazione ID casuale
Un operatore di assegnazione ID casuale produce un output leggendo le righe di input e aggiungendo un numero casuale a ogni riga. Funziona con 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 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 campione, potrebbe variare ogni volta che la query viene eseguita, anche se la query è la stessa.
Questo è il piano di esecuzione:
In questo piano di esecuzione, l'operatore Random Id Assign
riceve l'input da un operatore dell'unione distribuita, che riceve l'input da una scansione dell'indice. L'operatore restituisce
le righe con ID casuali e l'operatore Filter
applica un predicato scalare agli ID casuali e restituisce circa il 10% delle righe.
Nell'esempio seguente viene utilizzato 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 campione, potrebbe variare ogni volta che la query viene eseguita, anche se la query è la stessa.
Questo è il piano di esecuzione:
In questo piano di esecuzione, l'operatore Random Id Assign
riceve l'input da un operatore dell'unione distribuita, 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.
Serializza risultato
Un operatore di serializza i risultati è un caso speciale dell'operatore di struct di computing 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 |
---|
[] |
[[Let's Get Back Together, COUNTRY], [Starting Again, ROCK]] |
[[Not About The Guitar, BLUES]] |
[] |
[] |
Questo è il piano di esecuzione:
L'operatore di risultato della serie crea un risultato che contiene, per ogni riga della tabella Singers
, un array di coppie SongName
e SongGenre
per le canzoni del cantante.
Ordina
Un operatore di ordinamento 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 |
CLASSICO |
PAESE |
ROCK |
ROCK |
ROCK |
Questo è il piano di esecuzione:
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 serializza i risultati.
Per limitare il numero di righe restituite, un operatore di ordinamento può avere facoltativamente i parametri LIMIT
e OFFSET
. Per gli scenari distribuiti, un operatore di ordinamento con
un operatore LIMIT
e/o OFFSET
viene 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, quindi applica il limite/l'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:
Il piano di esecuzione mostra il limite locale per i server remoti e il limite globale per il server radice.
TVF
Un operatore di funzione con valori di tabella produce un 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 ne restituisce meno.
Ad esempio, utilizzando questa query:
SELECT Genre, SongName
FROM ML.PREDICT(MODEL GenreClassifier, Table Songs)
Ecco i risultati:
Genere | SongName |
---|---|
Paese | Non una chitarra |
Roccia | La seconda volta |
Pop | Ricomincia |
Pop | Niente è uguale |
Paese | Riscopriamoci insieme |
Pop | Sapevo che eri magia |
Elettronici | Blu |
Roccia | 42 |
Roccia | Storia di un combattimento |
Questo è il piano di esecuzione:
Input unione
Un operatore unione input restituisce i risultati a un operatore union all. Consulta l'operatore union all per un esempio di operatore di input di unione in un piano di esecuzione.
Operatori binari
Un operatore binario è un operatore che ha due figli relazionali. I seguenti operatori sono operatori binari:
- Applicazione incrociata
- Hash join
- Unione di unione
- Push hash join di trasmissione
- Applicazione esterna
Applica incrociato
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 della tabella. Gli operatori Cross apply e outer apply eseguono l'elaborazione orientata alle righe, a differenza di quelli 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 dal lato di 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;
Nella query vengono richiesti il nome di ogni cantante, oltre al nome di una sola delle sue canzoni.
Ecco i risultati:
FirstName | Non specificato |
---|---|
Alice | Non una chitarra |
Catalina | Riscopriamoci insieme |
Davide | NULLA |
Lisa | NULLA |
Marco | NULLA |
La prima colonna viene compilata dalla tabella Singers
e la seconda colonna viene
compilata dalla tabella Songs
. Nei casi in cui SingerId
esisteva nella tabella Singers
, ma non era presente SingerId
corrispondente nella tabella Songs
, la seconda colonna contiene NULL
.
Questo è il piano di esecuzione:
Il nodo di primo livello è un operatore unione distribuita. L'operatore di unione distribuita distribuisce i sottopiani ai server remoti. Il sottopiano contiene un operatore di serializza i risultati, che calcola il nome del cantante e il nome di una delle canzoni del cantante e serializza ogni riga dell'output.
L'operatore di risultato della serializzazione riceve l'input da un operatore di applicazione incrociata.
Il lato di input dell'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 dell'indice
SongsBySingerAlbumSongNameDesc
.
L'operatore di applicazione incrociata mappa ogni riga dal lato di input a una riga nel lato 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 su
SingerId
.) L'operatore di unione distribuita nella parte superiore del piano di esecuzione combina tutte le righe di output dei server remoti e le restituisce come risultati della query.
Join hash
Un operatore di hash join è un'implementazione basata su hash dei join SQL. Gli hash si uniscono all'elaborazione basata su set. L'operatore di join hash legge le righe dall'input contrassegnato come build e le inserisce in una tabella hash in base a una condizione di join. L'operatore hash join 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 una chitarra |
Verde | La seconda volta |
Verde | Ricomincia |
Verde | Niente è uguale |
Verde | Riscopriamoci insieme |
Verde | Sapevo che eri magia |
Verde | Blu |
Verde | 42 |
Terrorizzato | Storia di un combattimento |
Questo è il piano di esecuzione:
Nel piano di esecuzione, la build è un'unione distribuita che
distribuisce le scansioni nella tabella Albums
. Probe è un operatore Union Distributed che distribuisce le scansioni sull'indice SongsBySingerAlbumSongNameDesc
.
L'operatore hash join 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
. L'operatore hash join legge tutte
le righe dal lato del probe. Per ogni riga del probe, l'operatore di join hash cerca 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 condizioni residue è costituito dai join di non uguaglianza. I piani di esecuzione dei join di hash possono essere complessi a causa della gestione della memoria e delle varianti di join. L'algoritmo di join hash principale è adattato per gestire le varianti interne, semi, anti ed outer join.
Unisci join
Un operatore di merge join è un'implementazione basata sull'unione dei join SQL. Entrambi i lati
del join producono righe ordinate in base alle colonne utilizzate nella condizione di join. Il join di unione consuma 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.
L'opzione Unisci join non viene selezionata automaticamente dall'ottimizzatore. Per utilizzare questo operatore, imposta il metodo di join su MERGE_JOIN
nel suggerimento relativo alla 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 | Riscopriamoci insieme |
Verde | Sapevo che eri magia |
Verde | Blu |
Verde | 42 |
Terrorizzato | Storia di un combattimento |
Niente da fare con me | Non una chitarra |
Questo è il piano di esecuzione:
In questo piano di esecuzione, il join di unione viene distribuito in modo che venga eseguito nel punto in cui si trovano i dati. Ciò consente inoltre di utilizzare il join di unione in questo esempio 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, l'analisi a sinistra della tabella Albums
avanza
ogni volta che il valore SingerId
, AlbumId
è relativamente inferiore alla scansione
dell'indice SongsBySingerAlbumSongNameDesc
a destra SingerId_1
, coppia AlbumId_1
.
Analogamente, il lato destro avanza ogni volta che è inferiore al lato sinistro. Questo 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;
Produce i seguenti risultati:
AlbumTitle | SongName |
---|---|
Rifiuti totali | La seconda volta |
Rifiuti totali | Ricomincia |
Rifiuti totali | Niente è uguale |
Rifiuti totali | Riscopriamoci insieme |
Rifiuti totali | Sapevo che eri magia |
Rifiuti totali | Blu |
Rifiuti totali | 42 |
Rifiuti totali | Non una chitarra |
Verde | La seconda volta |
Verde | Ricomincia |
Verde | Niente è uguale |
Verde | Riscopriamoci insieme |
Verde | Sapevo che eri magia |
Verde | Blu |
Verde | 42 |
Verde | Non una 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 | Riscopriamoci 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 una chitarra |
Gioca | La seconda volta |
Gioca | Ricomincia |
Gioca | Niente è uguale |
Gioca | Riscopriamoci insieme |
Gioca | Sapevo che eri magia |
Gioca | Blu |
Gioca | 42 |
Gioca | Non una chitarra |
Terrorizzato | Storia di un combattimento |
Questo è il piano di esecuzione:
Nel piano di esecuzione precedente, l'ottimizzatore di query ha introdotto altri operatori Sort
per ottenere le proprietà necessarie all'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 l'ordinamento locale anziché a livello globale, il 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 dove compaiono condizioni residue è in join non di uguaglianza). I piani di esecuzione dei join di unione possono essere complessi a causa di requisiti di ordinamento aggiuntivi. L'algoritmo di join principale è adattato per gestire le varianti inner, semi, anti ed outer join.
Push del join di hash della trasmissione
Un operatore push hash join di trasmissione è un'implementazione distribuita basata su hash-join di join SQL. L'operatore di join hash per la trasmissione push legge le righe dal lato di input per creare un batch di dati. Quel batch viene poi trasmesso a tutti i server contenenti dati sul lato della mappa. Sui server di destinazione in cui viene ricevuto il batch di dati, viene creato un join hash utilizzando il batch come dati lato build e i dati locali vengono quindi analizzati come lato probe del join hash.
L'ottimizzatore non seleziona automaticamente Push hash join di trasmissione. Per utilizzare questo operatore, imposta il metodo di join su PUSH_BROADCAST_HASH_JOIN
nel suggerimento di 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 | Riscopriamoci insieme |
Verde | Sapevo che eri magia |
Verde | Blu |
Verde | 42 |
Terrorizzato | Storia di un combattimento |
Niente da fare con me | Non una chitarra |
Questo è il piano di esecuzione:
L'input per l'hash join della trasmissione push è l'indice AlbumsByAlbumTitle
.
Questo input è serializzato in un batch di dati. Quel batch viene poi inviato a tutte le suddivisioni locali dell'indice SongsBySingerAlbumSongNameDesc
, dove il batch viene poi deserializzato e integrato 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 condizioni residue è costituito dai join di non uguaglianza.
Applicazione esterna
Un operatore outer apply è simile a un operatore cross apply, tranne che un operatore outer apply garantisce che ogni esecuzione sul lato mappa restituisca almeno una riga producendo una riga con riempimento NULL, se necessario. (In altre parole, fornisce la semantica del left outer join.)
Operatori N-ari
Un operatore N-ary è un operatore con più di due figli relazionali. I seguenti operatori sono operatori n-ari:
Unisci tutto
Un operatore union all combina tutti i set di righe dei relativi elementi figlio senza rimuovere i duplicati. Union, tutti gli operatori ricevono l'input da 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:
L'operatore Union All combina le righe di input e, in questo esempio, invia i risultati a un operatore di serialize result.
Una query come la seguente avrebbe avuto esito positivo perché per ogni colonna viene utilizzato lo stesso insieme di tipi di dati, 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 andrebbe a buon fine, perché gli elementi 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, se 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 |
Lisa | 0 |
Marco | 0 |
Questo è il piano di esecuzione:
Il piano di esecuzione contiene una sottoquery scalare, mostrata come Sottoquery scalabile, sopra un operatore aggregate.
A volte Spanner converte sottoquery scalari in un altro operatore, come join o applicazione incrociata, 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;
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:
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, ad eccezione del 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 consumata.
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 valore AlbumId
. Il piano di esecuzione contiene una sottoquery di array, indicata come Sottoquery di array sopra un operatore di unione distribuita:
Operatori distribuiti
Gli operatori descritti in precedenza in questa pagina vengono eseguiti entro i limiti di una singola macchina. Gli operatori distribuiti vengono eseguiti su più server.
I seguenti operatori sono operatori distribuiti:
- Unione distribuita
- Unione di unione distribuita
- Applicazione incrociata distribuita
- Applicazione distribuita esternamente
- Applicare mutazioni
L'operatore di unione distribuita è l'operatore primitivo da cui vengono derivati Distributed Cross apply e Distributed Outer apply.
Gli operatori distribuiti appaiono nei piani di esecuzione con una variante unione distribuita oltre a una o più varianti unione distribuita locale. Una variante di un'unione distribuita esegue la distribuzione remota dei sottopiani. Una variante di un'unione distribuita locale viene aggiunta a ogni analisi eseguita per la query, come mostrato in questo piano di esecuzione:
Le varianti dell'unione distribuita locale garantiscono un'esecuzione stabile delle query al momento dei riavvii, per la modifica dinamica dei confini della suddivisione.
Quando possibile, una variante di un'unione distribuita ha un predicato di suddivisione che determina un'eliminazione delle suddivisioni, il che significa che i server remoti eseguono sottopiani solo sulle suddivisioni che soddisfano il predicato. Ciò migliora sia la latenza che le prestazioni generali delle query.
Unione distribuita
Un operatore di unione distribuita divide concettualmente una o più tabelle in più split, valuta in remoto una sottoquery in modo indipendente su ogni suddivisione, 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 | ROCK |
La seconda volta | ROCK |
Storia di un combattimento | ROCK |
Questo è il piano di esecuzione:
L'operatore Distributed Union invia 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 di serializza il risultato calcola i valori SongName
e SongGenre
dalle righe restituite dalle analisi della tabella. L'operatore di unione distribuita restituisce quindi i risultati combinati dei server remoti sotto forma di risultati della query SQL.
Unione di unione distribuita
L'operatore di unione distribuita distribuisce una query su più server remoti. Combina quindi i risultati della query per produrre un risultato ordinato, noto come ordinamento di unione distribuito.
Un'unione distribuita esegue i seguenti passaggi:
Il server radice invia una sottoquery a ogni server remoto che ospita una suddivisione dei dati oggetto della query. La sottoquery include istruzioni che i risultati sono ordinati in un ordine specifico.
Ogni server remoto esegue la sottoquery nella propria suddivisione, quindi invia i risultati nell'ordine richiesto.
Il server radice unisce la sottoquery ordinata per produrre un risultato ordinato completamente.
L'unione di unione distribuita è attivata, per impostazione predefinita, per Spanner versione 3 e successive.
Applicazione incrociata distribuita
Un operatore Distributed Cross Apply (DCA) estende l'operatore cross apply eseguendola su più server. I gruppi sul lato di input DCA 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 in esecuzione 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 |
Rifiuti totali |
Verde |
Questo è il piano di esecuzione:
L'input DCA contiene una scansione dell'indice nell'indice SongsBySingerAlbumSongNameDesc
che raggruppa righe di AlbumId
.
Il lato mappa per questo operatore di applicazione incrociata è una scansione dell'indice nell'indice AlbumsByAlbumTitle
, soggetta al predicato di AlbumId
nella riga di input corrispondente alla chiave AlbumId
nell'indice AlbumsByAlbumTitle
. La mappatura restituisce SongName
per i valori SingerId
nelle righe di input in batch.
Per riepilogare il processo DCA per questo esempio, l'input del DCA corrisponde alle righe raggruppate
in batch della tabella Albums
, mentre 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'esecuzione su più server, in modo simile a un operatore cross apply distribuito che 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:
LastName | ConcertDate |
---|---|
Trentor | 2014-02-18 |
Smith | 2011-09-03 |
Smith | 2010-06-06 |
Lomond | 2005-04-30 |
Martina | 2015-11-04 |
Richards |
Questo è il piano di esecuzione:
Applicare mutazioni
Un operatore apply mutations applica alla tabella le mutazioni di un'istruzione DML. È 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:
Informazioni aggiuntive
In questa sezione vengono descritti 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 operatori tecnicamente, ma non operatori separati nel piano di query.
Costruttore infrastruttura
Un costruttore di struct crea uno struct, ovvero 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 invece visualizzato negli operatori compute struct o serialize result.
Per un'operazione di calcolo di struct, il costruttore di struct crea uno struct in modo che le colonne per le righe calcolate possano utilizzare un riferimento di variabile singolo allo struct.
Per un'operazione di serializzazione dei risultati, il costruttore dello 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:
Nel piano di esecuzione, i costruttori di struct vengono visualizzati all'interno di un operatore dei risultati di serializzazione.