Questa pagina descrive i dettagli degli operatori utilizzati nei piani di esecuzione 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 a foglia è un operatore che non ha elementi secondari. I tipi di operatori basali sono:
Estrazione da un array
Un operatore di estrazione da un array appiattisce un array di input in righe di elementi. Ogni riga risultante contiene fino a due colonne: il valore effettivo dell'array e, facultativamente, 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 unità
La relazione unità restituisce una riga. Si tratta di 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 righe. Si tratta di un caso speciale dell'operatore genera relazione.
Ad esempio, utilizzando questa query:
SELECT *
FROM albums
LIMIT 0
Il risultato è:
Nessun risultato
Questo è il piano di esecuzione:
Scansione
Un operatore scan restituisce righe scansionando un'origine di righe. Di seguito sono riportati i tipi di operatori di ricerca:
- Scansione della tabella: la scansione viene eseguita in una tabella.
- Scansione dell'indice: la scansione viene eseguita su un indice.
- Scansione batch: la scansione viene eseguita sulle tabelle intermedie create da altri operatori relazionali (ad esempio una tabella creata da un applicazione distribuita incrociata).
Se 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 sotto forma di KeyPredicate: column=value
.
Nel peggiore dei casi, una query potrebbe dover cercare tutte le righe di una tabella. Questa situazione comporta una ricerca 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:
Nel piano di esecuzione, l'operatore unione distribuita di primo livello invia sottopiani ai server remoti. Ogni sottopiano ha un operatore serializza
risultato e un operatore di scansione dell'indice. Il predicato
Key Predicate: FirstName = 'Catalina'
limita la ricerca alle righe dell'indice
SingersByFirstLastname
con FirstName
uguale a Catalina
. L'output della scansione dell'indice viene restituito all'operatore di serializzazione del risultato.
Operatori unari
Un operatore unario è un operatore con un singolo elemento figlio relazionale.
I seguenti operatori sono operatori unari:
- Aggregate
- Applicare le mutazioni
- Crea batch
- Computing
- Strutture di calcolo
- DataBlockToRowAdapter
- Filtro
- Filtra ricerca
- Limite
- Unione con suddivisione locale
- Assegnazione ID casuale
- RowToDataBlockAdapter
- Eseguire la serializzazione del risultato
- Ordina
- TVF
- Input unione
Aggregazione
Un operatore aggregato implementa istruzioni SQL GROUP BY
e funzioni aggregate (ad esempio COUNT
). L'input per un operatore aggregato è suddiviso logicamente in gruppi organizzati in base alle colonne chiave (o in un unico gruppo se GROUP BY
non è presente). Per ogni gruppo vengono calcolati zero o più valori 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 | 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 precedente mostra un aggregato basato su stream. Gli aggregati basati su stream leggono da input già preordinati (se è presente GROUP BY
) e calcolano il gruppo senza bloccarsi. Gli aggregati basati su hash creano tabelle hash per mantenere contemporaneamente gli aggregati incrementali di più righe di input. Gli aggregati basati su stream sono più rapidi e utilizzano meno memoria rispetto agli aggregati basati su hash, ma richiedono l'ordinamento dell'input (per colonne chiave o 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 proprie righe di input e 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 di manipolazione dei dati (DML) 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:
Creazione batch
Un operatore create batch raggruppa le righe di input in una sequenza. Un'operazione di creazione di un batch avviene in genere nell'ambito di un'operazione di applicazione distribuita. Le righe di input possono essere riordinate durante l'aggregazione. Il numero di righe di input raggruppate in batch in ogni esecuzione dell'operatore batch varia.
Consulta l'operatore join distribuito per un esempio di operatore di batch di creazione in un piano di esecuzione.
Computing
Un operatore calcolo produce un 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.
Struttura di calcolo
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:
Nel piano di esecuzione, l'operatore sottoquery array riceve input da un operatore unione distribuita, che riceve input da un operatore di calcolo della struttura. L'operatore calcola la struttura dalle colonne SongName
e SongGenre
nella tabella Songs
.
DataBlockToRowAdapter
Un operatore DataBlockToRowAdapter
viene inserito automaticamente dall'ottimizzatore delle query di Spanner tra una coppia di operatori che operano utilizzando metodi di esecuzione diversi. Il suo input è un operatore che utilizza il metodo di esecuzione batch e il suo output viene inviato a un operatore che esegue il metodo di esecuzione orientato alle righe. Per ulteriori informazioni, consulta
Ottimizzare l'esecuzione delle query.
Filtro
Un operatore filter legge tutte le righe del relativo input, applica un predicato scalare su 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:
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 le prestazioni, ogni volta che un filtro è posizionato direttamente sopra una scansione,
influisce sulla modalità di lettura dei dati. Ad esempio, considera una tabella con chiave k
.
Un filtro con il predicato k = 5
direttamente sopra una scansione della tabella cerca le righe corrispondenti 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%'
.
Filtra scansione
Un operatore di scansione filtrata è sempre sopra una scansione di tabelle o indici. Lavora con la scansione per ridurre il numero di righe lette dal database e la scansione risultante è in genere più rapida rispetto a un filtro. Spanner applica la scansione dei filtri in determinate condizioni:
- Condizione cercabile: la condizione cercabile 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 principale. Ad esempio, se la chiave primaria è composta da
Col1
eCol2
, è possibile eseguire ricerche con una clausolaWHERE
che include valori espliciti perCol1
o perCol1
eCol2
. In questo caso, Spanner legge i dati solo 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:
Cognome |
---|
Richards |
Questo è il piano di esecuzione:
Limite
Un operatore limit limita 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 separato in una coppia locale-globale. Ogni
server remoto applica il limite locale per le proprie righe di output, quindi restituisce i suoi 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:
Il limite locale è il limite per ogni server remoto. Il server radice aggrega le righe dei server remoti e applica il limite globale.
Assegnazione ID casuale
Un operatore di assegnazione di ID casuale produce 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 percento.
SELECT s.songname
FROM songs AS s TABLESAMPLE bernoulli (10 PERCENT);
Ecco i risultati:
SongName |
---|
Ricominciare |
Nothing Is The Same |
Tieni presente che, poiché il risultato è un campione, potrebbe variare ogni volta che viene eseguita la query, anche se è la stessa.
Questo è il piano di esecuzione:
In questo piano di esecuzione, l'operatore Random Id Assign
riceve l'input da un operatore di unione distribuita, che riceve l'input da un'analisi 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.
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 |
---|
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 stessa.
Questo è il piano di esecuzione:
In questo piano di esecuzione, l'operatore Random Id Assign
riceve l'input da un operatore di unione distribuita, che riceve l'input da un'analisi 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 di suddivisione
Un operatore unione di suddivisioni locali trova la tabella splits archiviata sul server locale, esegue una sottoquery su ogni suddivisione e poi crea un'unione che combina tutti i risultati.
Un'unione con suddivisione locale viene visualizzata nei piani di esecuzione che eseguono la scansione di una tabella di posizionamenti. I posizionamenti possono aumentare il numero di suddivisioni in una tabella, rendendo più efficiente la scansione delle suddivisioni in batch in base alle relative 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:
L'unione distribuita invia una sottoquery a ogni batch di suddivisioni memorizzate fisicamente nello stesso server. Su ogni server, l'unione di suddivisioni locali trova le suddivisioni che memorizzano i dati Singers
, esegue la sottoquery su ogni suddivisione e restituisce i risultati combinati. In questo modo, l'unione distribuita e l'unione con suddivisione locale lavorano insieme per eseguire la scansione della tabella Singers
in modo efficiente.
Senza un'unione di suddivisioni locali, l'unione distribuita invierà un'RPC per suddivisione
invece che per batch di suddivisione, con un conseguente numero di viaggi RPC ridondanti quando esistono
più suddivisioni per batch.
RowToDataBlockAdapter
Un operatore RowToDataBlockAdapter
viene inserito automaticamente dall'ottimizzatore delle query di 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 inviato a un operatore che esegue il metodo di esecuzione batch. Per ulteriori informazioni, consulta
Ottimizzare l'esecuzione delle query.
Serializza il risultato
Un operatore serializza risultato è un caso speciale dell'operatore di calcolo della struttura che serializza ogni riga del risultato finale della query per restituirla al cliente.
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 di serializzazione 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 |
CLASSICO |
PAESE |
ROCCO |
ROCCO |
ROCCO |
Questo è il piano di esecuzione:
In questo piano di esecuzione, l'operatore di ordinamento riceve le righe di input da un operatore unione distribuita, le ordina e restituisce le righe ordinate a un operatore serializza risultato.
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 per le 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/l'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:
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 valore di tabella produce un output leggendo le righe di input e applica 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 | Nothing Is The Same |
Paese | Let's Get Back Together |
Pop | I Knew You Were Magic |
Elettronica | Blu |
Rock | 42 |
Rock | Fight Story |
Questo è il piano di esecuzione:
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 unione in un piano di esecuzione.
Operatori binari
Un operatore binario è un operatore con due elementi figlio di tipo relazionale. I seguenti operatori sono operatori binari:
- Applicazione incrociata
- Unione con hash
- Unione interna
- Unione hash di trasmissione push
- Applicazione esterna
- Unione ricorsiva
Applicazione incrociata
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 in base alle righe, a differenza degli operatori che eseguono l'elaborazione basata su set, come il join hash . L'operatore applica tra incroci ha due input, input e map. L'operatore Cross Apply applica ogni riga del lato di input al lato mappa. Il risultato dell'applicazione incrociata contiene 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;
La query richiede il nome di ogni cantante, insieme al nome di un solo brano del cantante.
Ecco i risultati:
Nome | Non specificato |
---|---|
Alice | Not About The Guitar |
Catalina | Let's Get Back Together |
David | NULL |
Lea | NULL |
Marc | NULL |
La prima colonna viene compilata dalla tabella Singers
e la seconda dalla tabella Songs
. Se nella tabella Singers
esiste un valore SingerId
, ma non esiste un valore SingerId
corrispondente nella tabella Songs
, la seconda colonna contiene NULL
.
Questo è il piano di esecuzione:
Il nodo di primo livello è un operatore di unione distribuita. L'operatore unione distribuita distribuisce i sottopiani ai server remoti. Il sottopiano contiene un operatore serializza risultato che calcola il nome del cantante e il nome di uno dei suoi brani e serializza ogni riga dell'output.
L'operatore di risultato di serializzazione riceve l'input da un operatore di applicazione incrociata.
Il lato di input dell'operatore applica tra tabelle è un'analisi della tabella sulla tabella Singers
.
Il lato mappa dell'operazione di applicazione incrociata contiene quanto segue (dall'alto verso il basso):
- Un operatore di aggregazione che restituisce
Songs.SongName
. - Un operatore limit che limita il numero di brani restituiti a uno per cantante.
- Una scansione dell'indice
SongsBySingerAlbumSongNameDesc
.
L'operatore Cross Apply mappa ogni riga dal lato di input a una riga nel lato mappa con lo stesso SingerId
. L'output dell'operatore applica tra tabelle è il valore FirstName
della riga di input e il valore SongName
della riga della mappa.
Il valore SongName
è NULL
se non esiste una riga della mappa che corrisponda a SingerId
. L'operatore unione distribuito nella parte superiore del piano di esecuzione combina quindi tutte le righe di output dei server remoti e le restituisce come risultati della query.
Join hash
Un operatore di join hash è un'implementazione basata su hash dei join SQL. I join con hash eseguono l'elaborazione basata su set. L'operatore di join hash legge le righe di input contrassegnate come build e le inserisce in una tabella hash in base a una condizione di join. L'operatore di join con hash legge quindi le righe dell'input contrassegnate come probe. Per ogni riga letta dall'input della sonda, l'operatore di join hash cerca le righe corrispondenti nella tabella hash. L'operatore di join con 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 |
---|---|
Nothing To Do With Me | Not About The Guitar |
Verde | La seconda volta |
Verde | Ricominciare |
Verde | Nothing Is The Same |
Verde | Let's Get Back Together |
Verde | I Knew You Were Magic |
Verde | Blu |
Verde | 42 |
Terrorizzato | Fight Story |
Questo è il piano di esecuzione:
Nel piano di esecuzione, build è un'unione distribuita che
distribuisce le analisi nella tabella Albums
. Probe è un operatore di unione distribuito che distribuisce le scansioni nell'indice SongsBySingerAlbumSongNameDesc
.
L'operatore di join con hash legge tutte le righe dal lato di compilazione. Ogni riga di compilazione viene inserita in una tabella hash in base alle colonne della condizione a.SingerId =
s.SingerId AND a.AlbumId = s.AlbumId
. Successivamente, l'operatore di join con hash legge tutte le righe dal lato della sonda. Per ogni riga di analisi, l'operatore di join con hashing cerca le corrispondenze nella tabella hash. Le corrispondenze risultanti vengono restituite dall'operatore di join con hashing.
Le corrispondenze risultanti nella tabella hash possono anche essere filtrate in base a una condizione residua prima di essere restituite. Un esempio di dove vengono visualizzate le condizioni residue è nei join con criteri diversi dall'uguaglianza. I piani di esecuzione delle unioni con hashing possono essere complessi a causa della gestione della memoria e delle varianti di unione. L'algoritmo di join hash principale è adattato per gestire le varianti di join interno, semi, anti e esterno.
Join di unione
Un operatore di join per unione è un'implementazione basata su unione di un join SQL. Entrambi i lati
dell'unione producono righe ordinate in base alle colonne utilizzate nella condizione di join. La join di unione utilizza contemporaneamente entrambi gli stream di input e genera 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'unione per accodamento non viene selezionata automaticamente dall'ottimizzatore. Per utilizzare questo
operatore, imposta il metodo di join su MERGE_JOIN
nell'indicazione della 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 | Nothing Is The Same |
Verde | Let's Get Back Together |
Verde | I Knew You Were Magic |
Verde | Blu |
Verde | 42 |
Terrorizzato | Fight Story |
Nothing To Do With Me | Not About The Guitar |
Questo è il piano di esecuzione:
In questo piano di esecuzione, l'unione per accodamento viene distribuita in modo che venga eseguita
dove si trovano i dati. Ciò consente inoltre all'unione per accodamento in questo esempio di operare senza l'introduzione di operatori di ordinamento aggiuntivi, perché entrambe le scansioni delle tabelle sono già ordinate in base a SingerId
, AlbumId
, che è la condizione di join. In questo piano, la scansione a sinistra della tabella Albums
avanza ogni volta che SingerId
, AlbumId
è relativamente inferiore alla coppia SingerId_1
, AlbumId_1
della scansione dell'indice SongsBySingerAlbumSongNameDesc
a destra.
Analogamente, il lato destro avanza ogni volta che è inferiore al lato
sinistra. Questo avanzamento dell'unione continua a cercare equivalenze in modo da poter restituire le corrispondenze risultanti.
Considera un altro esempio di join 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;
e genera i seguenti risultati:
AlbumTitle | SongName |
---|---|
Rifiuti totali | La seconda volta |
Rifiuti totali | Ricominciare |
Rifiuti totali | Nothing Is The Same |
Rifiuti totali | Let's Get Back Together |
Rifiuti totali | I Knew You Were Magic |
Rifiuti totali | Blu |
Rifiuti totali | 42 |
Rifiuti totali | Not About The Guitar |
Verde | La seconda volta |
Verde | Ricominciare |
Verde | Nothing Is The Same |
Verde | Let's Get Back Together |
Verde | I Knew You Were Magic |
Verde | Blu |
Verde | 42 |
Verde | Not About The Guitar |
Nothing To Do With Me | La seconda volta |
Nothing To Do With Me | Ricominciare |
Nothing To Do With Me | Nothing Is The Same |
Nothing To Do With Me | Let's Get Back Together |
Nothing To Do With Me | I Knew You Were Magic |
Nothing To Do With Me | Blu |
Nothing To Do With Me | 42 |
Nothing To Do With Me | Not About The Guitar |
Gioca | La seconda volta |
Gioca | Ricominciare |
Gioca | Nothing Is The Same |
Gioca | Let's Get Back Together |
Gioca | I Knew You Were Magic |
Gioca | Blu |
Gioca | 42 |
Gioca | Not About The Guitar |
Terrorizzato | Fight Story |
Questo è il piano di esecuzione:
Nel piano di esecuzione precedente, l'ottimizzatore delle query ha introdotto operatori Sort
aggiuntivi per ottenere le proprietà necessarie per l'esecuzione dell'unione per accodamento. La condizione JOIN
nella query di questo esempio si basa solo su
AlbumId
, che non è il modo in cui vengono archiviati i dati, pertanto è necessario aggiungere un criterio di ordinamento. Il motore di query supporta un algoritmo di unione distribuita, che consente di eseguire la ordinamento localmente anziché a livello globale, distribuendo e parallelizzando 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 è nelle unioni con diseguaglianza. I piani di esecuzione delle unioni merge possono essere complessi a causa di requisiti di ordinamento aggiuntivi. L'algoritmo di join per unione principale è adattato per gestire le varianti di join interno, semi, anti e esterno.
Join hash trasmissione push
Un operatore di join hash push broadcast è un'implementazione distribuita basata su join hash dei join SQL. L'operatore di join hash di trasmissione push legge le righe dal lato di input per creare un batch di dati. Il batch viene poi trasmesso a tutti i server contenenti dati lato mappa. Sui server di destinazione in cui viene ricevuto il batch di dati, viene creata un'unione hash utilizzando il batch come dati lato build e i dati locali vengono quindi analizzati come lato di prova dell'unione hash.
L'unione hash di trasmissione push non viene selezionata automaticamente dall'ottimizzatore. 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 | Ricominciare |
Verde | Nothing Is The Same |
Verde | Lets Get Back Together |
Verde | I Knew You Were Magic |
Verde | Blu |
Verde | 42 |
Terrorizzato | Fight Story |
Nothing To Do With Me | Not About The Guitar |
Questo è il piano di esecuzione:
L'input per l'unione hash di trasmissione push è l'indice AlbumsByAlbumTitle
.
L'input viene serializzato in un batch di dati. Il batch viene poi inviato a tutte le suddivisioni locali dell'indice SongsBySingerAlbumSongNameDesc
, dove viene deserializzato e inserito in una tabella hash. La tabella hash utilizza quindi
i dati dell'indice locale come sonda per restituire le corrispondenze risultanti.
Le corrispondenze risultanti potrebbero anche essere filtrate in base a una condizione residua prima di essere riportate. Un esempio di dove vengono visualizzate le condizioni residue è nelle unioni con diseguaglianza.
Applicazione esterna
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 con valori NULL se necessario. In altre parole, fornisce la semantica del join esterno sinistro.
Unione ricorsiva
Un operatore di unione ricorsiva esegue l'unione di due input, uno che rappresenta un caso base
e l'altro che rappresenta un caso recursive
. Viene utilizzato
nelle query sui grafici con attraversamenti di percorsi quantificati. L'input di base viene elaborato pirmo e esattamente una volta. L'input ricorsivo viene elaborato fino al termine della ricorsione. La ricorsione termina quando viene raggiunto il limite superiore, se specificato, o quando la ricorsione non produce nuovi risultati. Nel
seguente esempio, la tabella Collaborations
viene aggiunta allo schema e viene creato un
grafo di 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 sul 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
L'operatore di unione ricorsiva filtra la tabella Singers
per trovare il cantante
con il valore SingerId
specificato. Questo è l'input di base per l'unione ricorsiva. L'input ricorsivo dell'unione ricorsiva è costituito da un join distribuito o da un altro operatore di join per altre query che uniscono ripetutamente la tabella Collaborations
ai risultati dell'iterazione precedente del join. Le righe dell'input di base formano
l'iterazione zero. A ogni iterazione, l'output dell'iterazione viene memorizzato dalla scansione ricorsiva della coda. Le righe della scansione ricorsiva del spool vengono unite alla tabella Collaborations
su spoolscan.featuredSingerId =
Collaborations.SingerId
. La ricorsione termina quando sono completate due iterazioni, poiché si tratta del limite superiore specificato nella query.
Operatori n-ari
Un operatore N-ario è un operatore con più di due elementi secondari relazionali. I seguenti operatori sono operatori N-ari:
Unisci tutto
Un operatore union all combina tutti gli insiemi di righe dei relativi figli senza rimuovere i duplicati. Gli operatori Union All ricevono i dati dagli operatori union input distribuiti su più server. L'operatore union all richiede che i relativi 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 le righe secondarie è 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 serializza risultato.
Una query come la seguente andrebbe a buon fine, perché viene utilizzato lo stesso insieme di tipi di dati per ogni colonna, 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é 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 scalare è una sottoespressione SQL che fa parte di un'espressione scalare. Spanner tenta di rimuovere le sottoquery scalari laddove possibile. Tuttavia, in alcuni scenari, 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 è l'espressione secondaria SQL:
SELECT Count(*)
FROM songs
WHERE duration > 300;
Ecco i risultati (della query completa):
Nome | |
---|---|
Alice | 1 |
Catalina | 0 |
David | 0 |
Lea | 0 |
Marc | 0 |
Questo è il piano di esecuzione:
Il piano di esecuzione contiene una sottoquery scalare, indicata come Sottoquery scalare, su un operatore aggregate.
A volte Spanner converte le sottoquery scalari in un altro operatore, ad esempio una join o un'applicazione incrociata, per migliorare le prestazioni.
Ad esempio, utilizzando questa query:
SELECT *
FROM songs
WHERE duration = (SELECT Max(duration)
FROM songs);
Questa è l'espressione secondaria SQL:
SELECT MAX(Duration)
FROM Songs;
Ecco i risultati (della query completa):
SingerId | AlbumId | TrackId | SongName | Durata | SongGenre |
---|---|---|---|---|---|
2 | 1 | 6 | Nothing Is The Same | 303 | BLUES |
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 array è simile a una sottoquery scalare, tranne per il fatto che la sottoquery è autorizzata a 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 rispetto a quel AlbumId
. Il piano di esecuzione contiene una sottoquery
di array, indicata come Sottoquery array, sopra un operatore di unione distribuito:
Operatori distribuiti
Gli operatori descritti in precedenza in questa pagina vengono eseguiti nei limiti di una singola macchina. Gli operatori distribuiti vengono eseguiti su più server.
I seguenti operatori sono operatori distribuiti:
- Unione distribuita
- Unione combinata distribuita
- Applicazione tra tabelle distribuita
- Applicazione esterna distribuita
- Applicare le mutazioni
L'operatore di unione distribuita è l'operatore primitivo da cui derivano gli operatori distribuiti incrociati e distribuiti esterni.
Gli operatori distribuiti vengono visualizzati nei piani di esecuzione con una variante di unione distribuita su una o più varianti di unione distribuita locale. Una variante di unione distribuita esegue la distribuzione remota dei sottopiani. Una variante dell'unione distribuita locale si trova sopra ciascuna delle analisi eseguite per la query, come mostrato in questo piano di esecuzione:
Le varianti dell'unione distribuita locale garantiscono un'esecuzione stabile delle query quando si verificano riavvii per i confini di suddivisione in modifica dinamica.
Ove possibile, una variante di unione distribuita ha un predicato di suddivisione che comporta la potatura delle suddivisioni, il che significa che i server remoti eseguono i sottopiani solo sulle suddivisioni che soddisfano il predicato. In questo modo, vengono migliorate sia la latenza sia le prestazioni complessive delle query.
Sindacato distribuito
Un operatore di unione distribuita suddivide concettualmente una o più tabelle in più suddivisioni, valuta da remoto una sottoquery in modo indipendente su ogni suddivisione 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 | ROCCO |
La seconda volta | ROCCO |
Fight Story | ROCCO |
Questo è il piano di esecuzione:
L'operatore unione distribuito invia sottopiani ai server remoti, che eseguono una scansione della tabella sulle 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 unione distribuito
quindi restituisce i risultati combinati dei server remoti come risultati della query SQL.
Join unione distribuita
L'operatore unione per unione distribuita 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 per unione distribuita esegue i seguenti passaggi:
Il server radice invia una sottoquery a ogni server remoto che ospita una suddivisione dei dati sottoposti a query. La sottoquery include istruzioni che ordinano i risultati in un ordine specifico.
Ogni server remoto esegue la sottoquery sulla propria suddivisione e restituisce i risultati nell'ordine richiesto.
Il server radice unisce la sottoquery ordinata per produrre un risultato completamente ordinato.
L'unione per unione distribuita è attiva per impostazione predefinita per Spanner 3 e versioni successive.
Operazione di join distribuita
Un operatore Distributed Cross Apply (DCA) estende l'operatore Cross Apply eseguendolo su più server. Il lato di input di DCA raggruppa lotti 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 |
Nothing To Do With Me |
Gioca |
Rifiuti totali |
Verde |
Questo è il piano di esecuzione:
L'input DCA contiene una scansione dell'indice sull'indice SongsBySingerAlbumSongNameDesc
che raggruppa le righe di AlbumId
. Il lato mappa per questo operatore di applicazione incrociata è una scansione dell'indice sull'indice AlbumsByAlbumTitle
, soggetto al predicato di AlbumId
nella riga di input che corrisponde alla chiave AlbumId
nell'indice AlbumsByAlbumTitle
. La mappatura
restituisce SongName
per i valori SingerId
nelle righe di input raggruppate.
Per riepilogare la procedura DCA per questo esempio, l'input della DCA è costituito dalle righe in batch della tabella Albums
e l'output della 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 eseguendolo 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:
Applicare le mutazioni
Un operatore apply mutations applica le mutazioni da un'istruzione di manipolazione dei dati (DML) 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:
Informazioni aggiuntive
Questa sezione descrive gli elementi che non sono operatori autonomi, ma che invece 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 una struct o una raccolta di campi. Generalmente 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 calcolo della struttura, il costruttore della struttura crea una struttura in modo che le colonne per le righe calcolate possano utilizzare un singolo riferimento di variabile alla struttura.
Per un'operazione di serializzazione dei risultati, il costruttore della struttura crea una struttura per eseguire la serializzazione dei 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 di serializzazione del risultato.