Operadores de execução de consulta

Introdução

Nesta página, descrevemos detalhes sobre os operadores usados nos planos de execução de consulta do Spanner. Para saber como recuperar um plano de execução de uma consulta específica usando o console do Google Cloud, consulte Noções básicas sobre como o Spanner executa consultas.

As consultas e os planos de execução dessa página se baseiam no esquema de banco de dados abaixo:

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

É possível usar as seguintes instruções da linguagem de manipulação de dados (DML) para adicionar dados a estas tabelas:

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

Operadores folha

Um operador folha é um operador que não tem filhos. Os tipos de operadores folha são:

Array unnest

Usando um operador array unnest é possível nivelar uma matriz de entrada em linhas de elementos. Em cada linha resultante há até duas colunas: o valor real da matriz e, opcionalmente, a posição baseada em zero na matriz.

Por exemplo, para esta consulta:

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

A consulta nivela a matriz [1,2,3] na coluna a e mostra a posição da matriz na coluna b.

Estes são os resultados:

a b
1 0
2 1
3 2

Este é o plano de execução:

operador array unnest

Generate relation

Um operador generate relation retorna zero ou mais linhas.

Unit relation

Unit relation retorna uma linha. É um caso especial do operador generate relation.

Por exemplo, para esta consulta:

SELECT 1 + 2 AS Result;

O resultado é:

Result
3

Este é o plano de execução:

operador unit relation

Empty relation

Empty relation não retorna nenhuma linha. É um caso especial do operador generate relation.

Por exemplo, para esta consulta:

SELECT * FROM Albums LIMIT 0

O resultado é:

No results

Este é o plano de execução:

operador empty relation

Scan

O operador scan retorna linhas por meio da verificação de uma fonte de linhas. Estes são os tipos de operadores scan:

  • Table scan: a verificação ocorre em uma tabela.
  • Index scan: a verificação ocorre em um índice.
  • Batch scan: a verificação ocorre em tabelas intermediárias criadas por outros operadores relacionais. Por exemplo, uma tabela criada por um operador distributed cross apply.

Sempre que possível, o Spanner aplica predicados simples em chaves como parte de uma verificação. As verificações são executadas de modo mais eficiente quando os predicados são aplicados porque não é necessário que a tabela ou índice inteiros sejam lidos pela verificação. Os predicados aparecem no plano de execução na forma KeyPredicate: column=value.

No pior dos casos, uma consulta pode precisar pesquisar todas as linhas de uma tabela. Essa situação leva a uma verificação completa e aparece no plano de execução como full scan: true.

Por exemplo, para esta consulta:

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

Estes são os resultados:

LastName
Smith

Este é o plano de execução:

operador scan

No plano de execução, com o operador distributed union de nível superior, são enviados subplanos a servidores remotos. Em cada subplano há um operador serialize result e um operador index scan. O predicado Key Predicate: FirstName = 'Catalina' restringe a verificação às linhas no índice SingersByFirstLastname que têm FirstName igual a Catalina. A saída de index scan é retornada para o operador serialize result.

Operadores unary

Um operador unary é um operador que tem um único filho relacional.

Os seguintes operadores são unários:

Aggregate

Um operador aggregate implementa GROUP BY instruções SQL e funções agregadas (como COUNT). A entrada de um operador de agregação é particionada logicamente em grupos organizados em colunas de chave (ou em um único grupo se GROUP BY não estiver presente). Zero ou mais agregados são computados para cada grupo.

Por exemplo, para esta consulta:

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

A consulta é agrupada por SingerId e executa uma agregação AVG e uma agregação COUNT.

Estes são os resultados:

SingerId average count
3 278 1
2 225.875 8

Este é o plano de execução:

operador aggregate

Os operadores aggregate podem ser baseados em stream ou baseados em hash. O plano de execução acima mostra um agregado baseado em stream. Os agregados baseados em stream leem a entrada já pré-classificada (se GROUP BY estiver presente) e computam os grupos sem bloqueio. Os agregados baseados em hash criam tabelas de hash para manter agregados incrementais de várias linhas de entrada simultaneamente. Os agregados baseados em stream são mais rápidos e usam menos memória do que os agregados baseados em hash, mas exigem que a entrada seja classificada (por colunas de chave ou índices secundários).

Para cenários distribuídos, um operador aggregate pode ser separado em um par local/global. A agregação local é executada individualmente pelos servidores remotos nas respectivas linhas de entrada e, em seguida, os resultados são retornados ao servidor raiz. No servidor raiz é executada a agregação global.

Apply mutations

Um operador apply mutations aplica as mutações de uma linguagem de manipulação de dados (DML, na sigla em inglês) à tabela. Ele é o operador principal em um plano de consulta para uma instrução DML.

Por exemplo, para esta consulta:

DELETE FROM Singers
WHERE FirstName = 'Alice';

Estes são os resultados:

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

Este é o plano de execução:

operador apply mutations

Create batch

Um operador create batch reúne as linhas de entrada em sequência. Uma operação create batch geralmente ocorre como parte de uma operação distributed cross apply. É possível reordenar as linhas de entrada durante o processamento de lotes. O número de linhas de entrada que são agrupadas em cada execução do operador de lote é variável.

Consulte o operador distributed cross apply para ver um exemplo de operador create batch em um plano de execução.

Compute

A saída de um operador compute é produzida com a leitura das linhas de entrada e a adição de uma ou mais colunas extras que são computadas por meio de expressões escalares. Consulte o operador union all para ver um exemplo do operador compute em um plano de execução.

Compute struct

Com um operador compute struct é possível criar uma variável para uma estrutura contendo campos para cada uma das colunas de entrada.

Por exemplo, para esta consulta:

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;

Estes são os resultados:

FirstName Unspecified
Alice [["Not About The Guitar","BLUES"]]

Este é o plano de execução:

operador compute struct

No plano de execução, o operador de subconsulta de matriz recebe a entrada de um operador de união distribuída, que recebe entrada de um operador comput struct. O operador comput struct cria uma estrutura das colunas SongName e SongGenre na tabela Songs.

Filtrar

Com um operador filter, todas as linhas da entrada são lidas, um predicado escalar é aplicado a cada linha e apenas as linhas que satisfazem ao predicado são retornadas.

Por exemplo, para esta consulta:

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

Estes são os resultados:

LastName
Richards

Este é o plano de execução:

operador filter

O predicado para cantores com sobrenome começado por Rich é implementado como um filtro. A entrada do filtro é a saída de um index scan e a saída do filtro são linhas em que LastName começa com Rich.

Para fins de desempenho, sempre que um filtro é posicionado diretamente acima de uma operação scan, o filtro afeta o modo como os dados são lidos. Por exemplo, considere uma tabela com a chave k. Um filtro com predicado k = 5 diretamente na parte superior de uma verificação da tabela procurará linhas que correspondam k = 5, sem ler toda a entrada. Assim, a execução da consulta fica mais eficiente. No exemplo acima, o operador de filtro lê somente as linhas que satisfazem o predicado WHERE s.LastName LIKE 'Rich%'.

Filter scan

Um operador filter scan está sempre acima de um table ou index scan. Ele opera com a verificação para reduzir o número de linhas lidas no banco de dados e a verificação resultante é normalmente mais rápida do que com um filter. O Spanner aplica a verificação de filtro em determinadas condições:

  • Condição pesquisável: a condição pesquisável se aplica se o Spanner puder determinar uma linha específica para acessar na tabela. Em geral, isso acontece quando o filtro está em um prefixo da chave primária. Por exemplo, se a chave primária consistir em Col1 e Col2, uma cláusula WHERE que inclua valores explícitos para Col1 ou Col1 e Col2 é pesquisável. Nesse caso, o Spanner lê os dados apenas dentro do intervalo de chaves.
  • Condição residual: qualquer outra condição em que o Spanner possa avaliar a verificação para limitar a quantidade de dados lidos.

Por exemplo, para esta consulta:

SELECT LastName
FROM Singers
WHERE SingerId = 1

Estes são os resultados:

LastName
Richards

Este é o plano de execução:

operador filter scan

Limite

Um operador limit restringe o número de linhas retornadas. Um parâmetro OFFSET opcional especifica a linha inicial a ser retornada. Em cenários distribuídos, é possível separar um operador "limit" em um par local/global. Em cada servidor remoto é aplicado o limite local às linhas de saída e os resultados são retornados ao servidor raiz. No servidor raiz as linhas enviadas pelos servidores remotos são agregadas e o limite global é aplicado.

Por exemplo, para esta consulta:

SELECT s.SongName
FROM Songs AS s
LIMIT 3;

Estes são os resultados:

SongName
Not About The Guitar
The Second Time
Starting Again

Este é o plano de execução:

operador limit

O limite local é aquele de cada servidor remoto. As linhas dos servidores remotos são agregadas pelo servidor raiz e, em seguida, o limite global é aplicado.

ID aleatório atribuído

Um operador aleatório de atribuição de id produz saída lendo suas linhas de entrada e adicionando um número aleatório a cada linha. Ele funciona com um operador Filter ou Sort para conseguir métodos de amostragem. Os métodos compatíveis de amostragem são Bernoulli e Reservoir.

Por exemplo, a consulta a seguir usa a amostragem Bernoulli com uma taxa de amostragem de 10%.

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

Estes são os resultados:

SongName
Starting Again
Nothing Is The Same

Observe que, por ser uma amostra, o resultado pode variar toda vez que a consulta for executada, embora a consulta seja a mesma.

Este é o plano de execução:

operador de amostra bernoulli

Neste plano de execução, o operador Random Id Assign recebe sua entrada de um operador de união distribuída, que recebe sua entrada a partir de um index scan. O operador retorna as linhas com códigos aleatórios, e o operador Filter aplica um predicado escalar aos códigos aleatórios e retorna aproximadamente 10% das linhas.

O exemplo a seguir usa a amostragem de reservatório com uma taxa de amostragem de 2 linhas.

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

Estes são os resultados:

SongName
I Knew You Were Magic
The Second Time

Observe que, por ser uma amostra, o resultado pode variar toda vez que a consulta for executada, embora a consulta seja a mesma.

Este é o plano de execução:

operador de amostra de reservatório

Neste plano de execução, o operador Random Id Assign recebe sua entrada de um operador de união distribuída, que recebe sua entrada a partir de um index scan. O operador retorna as linhas com códigos aleatórios, e o operador Sort aplica a ordem de classificação nos IDs aleatórios e aplica LIMIT com duas linhas.

Serialize result

Um operador serialize result é um caso especial do operador "compute struct" que serializa cada linha do resultado final da consulta para retorno ao cliente.

Por exemplo, para esta consulta:

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

A consulta pede uma matriz de SongName e SongGenre com base em SingerId.

Estes são os resultados:

Não especificado
[]
[[Let's Get Back Together, COUNTRY], [Starting Again, ROCK]]
[[Not About The Guitar, BLUES]]
[]
[]

Este é o plano de execução:

Operador serialize result

O operador serialize result cria um resultado que contém, para cada linha da tabela Singers, uma matriz de pares SongName e SongGenre para as músicas de um cantor.

Sort

Um operador sort lê as linhas de entrada, as ordena por colunas e, em seguida, retorna os resultados classificados.

Por exemplo, para esta consulta:

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

Estes são os resultados:

SongGenre
BLUES
BLUES
BLUES
BLUES
CLASSICAL
COUNTRY
ROCK
ROCK
ROCK

Este é o plano de execução:

operador sort

Neste plano de execução, as linhas de entrada de um operador "sort" são recebidas de um distributed union e classificadas. Em seguida, elas são retornadas a um operador serialize result.

Para restringir o número de linhas retornadas, um operador "sort" pode opcionalmente ter os parâmetros LIMIT e OFFSET. Em cenários distribuídos, um operador "sort" com um operador LIMIT e/ou OFFSET é separado em um par local/global. Cada servidor remoto aplica a ordem de classificação e o limite/deslocamento local para as linhas de entrada e, em seguida, retorna os resultados ao servidor raiz. O servidor raiz agrega as linhas enviadas pelos servidores remotos, as classifica e, em seguida, aplica o limite/deslocamento global.

Por exemplo, para esta consulta:

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

Estes são os resultados:

SongGenre
BLUES
BLUES
BLUES

Este é o plano de execução:

operador sort com limit

O plano de execução mostra o limite local dos servidores remotos e o limite global do servidor raiz.

TVF

Um operador de função com valor de tabela produz saídas lendo as linhas de entrada e aplicando a função especificada. A função pode implementar o mapeamento e retornar o mesmo número de linhas da entrada. Ele também pode ser um gerador que retorna mais linhas ou um filtro que retorna menos linhas.

Por exemplo, para esta consulta:

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

Estes são os resultados:

Gênero SongName
País Not About The Guitar
De balanço The Second Time
Pop Starting Again
Pop Nothing Is The Same
País Let's Get Back Together
Pop I Knew You Were Magic
Eletrônica Azul
De balanço 42
De balanço Fight Story

Este é o plano de execução:

Operador tvf

Union input

Um operador union input retorna resultados para o operador union all. Consulte o operador union all para ver um exemplo do operador "union input" em um plano de execução.

Operadores binários

Um operador binário é um operador que tem dois filhos relacionais. Os seguintes operadores são binários:

Cross apply

Um operador cross apply executa uma consulta de tabela em cada linha recuperada por uma consulta de outra tabela e retorna a união de todas as execuções da consulta de tabela. Os operadores "cross apply" e outer apply executam o processamento orientado por linha, em vez do processamento baseado em conjunto, como hash join, que é executado por outros operadores. O operador cross apply tem duas entradas, input e map. O operador cross apply aplica cada linha do lado "input" ao lado "map". O resultado de "cross apply" tem colunas de ambos os lados: "input" e "map".

Por exemplo, para esta consulta:

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

A consulta pede o nome de cada cantor, juntamente com o nome de apenas uma das canções do cantor.

Estes são os resultados:

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

A primeira coluna é preenchida a partir da tabela Singers e a segunda coluna é preenchida a partir da tabela Songs. Caso haja SingerId na tabela Singers, mas não exista SingerId correspondente na tabela Songs, a segunda coluna conterá NULL.

Este é o plano de execução:

operador cross apply

O nó de nível superior é um operador distributed union. O operador "distributed union" distribui subplanos a servidores remotos. O subplano contém um operador serialize result, que computa o nome do cantor e o nome de uma das músicas dele e serializa cada linha da saída.

O operador "serialize result" recebe a entrada de um operador "cross apply". O lado "input" do operador "cross apply" é uma operação scan na tabela Singers.

O lado "map" da operação "cross apply" contém os seguintes itens (de cima para baixo):

  • Um operador aggregate que retorna Songs.SongName.
  • Um operador limit que limita o número de canções retornadas a uma por cantor.
  • Um operador index scan no índice SongsBySingerAlbumSongNameDesc.

O operador "cross apply" associa cada linha do lado "input" a uma linha do lado "map" que tenha o mesmo SingerId. A saída do operador "cross apply" é o valor FirstName da linha de "input" e o valor SongName da linha de "map". (O valor SongName será NULL se não houver linha de mapa que corresponda a SingerId.) O operador de união distribuída na parte superior do plano de execução combina todas as linhas de saída dos servidores remotos e as retorna como os resultados da consulta.

Hash join

Um operador hash join é uma implementação baseada em hash de junções SQL. Operações "hash join" executam processamento baseado em conjunto. O operador "hash join" lê as linhas da entrada marcadas como build e as insere em uma tabela de hash com base em uma condição de junção. Em seguida, o operador "hash join" lê as linhas da entrada marcadas como probe. Para cada linha que lê a partir da entrada "probe", o operador "hash join" procura as linhas correspondentes na tabela de hash. O operador "hash join" retorna as linhas correspondentes como resultado.

Por exemplo, para esta consulta:

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;

Estes são os resultados:

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

Este é o plano de execução:

operador hash join

No plano de execução, build é um distributed union que distribui scans na tabela Albums. Probe é um operador "distributed union" que distribui "scans" no índice SongsBySingerAlbumSongNameDesc. O operador "hash join" lê todas as linhas do lado "build". Cada linha "build" é colocada em uma tabela de hash com base nas colunas da condição a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId. Em seguida, o operador "hash join" lê todas as linhas do lado "probe". Para cada linha "probe", o operador "hash join" procura correspondências na tabela de hash. As correspondências resultantes são retornadas pelo operador "hash join".

As correspondências resultantes na tabela de hash também podem ser filtradas por uma condição residual antes de serem retornadas. Um exemplo de onde as condições residuais aparecem é em junções de não igualdade. Os planos de execução de "hash join" podem ser complexos devido ao gerenciamento de memória e às variantes de junção. O principal algoritmo de "hash join" é adaptado para lidar com as variantes "inner", "semi", "anti" e "outer join".

Mesclar junção

Um operador merge join é uma implementação baseada em mesclagem de mesclagem SQL. Os dois lados da junção produzem linhas ordenadas pelas colunas usadas na condição de junção. A mesclagem de mesclar consome os streams de entrada simultaneamente e gera linhas quando a condição da junção é atendida. Se as entradas não forem classificadas originalmente conforme necessário, o otimizador adicionará operadores Sort explícitos ao plano.

Merge join não é selecionado automaticamente pelo otimizador. Para usar esse operador, defina o método de junção como MERGE_JOIN na dica de consulta, conforme mostrado no exemplo a seguir:

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;

Estes são os resultados:

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

Este é o plano de execução:

operador merge join_1

Neste plano de execução, a mesclagem é distribuída para que seja executada onde os dados estão localizados. Isso também permite que a mesclagem seja executada neste exemplo sem a introdução de outros operadores de classificação, já que as duas verificações de tabela já estão classificadas por SingerId, AlbumId, que é a união. condition. Neste plano, a verificação do lado esquerdoAlbums a tabela avança sempre queSingerId ,AlbumId é relativamente menor que o lado direitoSongsBySingerAlbumSongNameDesc verificação de índiceSingerId_1 ,AlbumId_1. Da mesma forma, o lado direito avança sempre que é menor que o esquerdo. Esse avanço da integração continua procurando equivalências, de modo que as correspondências resultantes possam ser retornadas.

Considere outro exemplo de mesclagem usando a seguinte consulta:

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

Ela produz os seguintes resultados:

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

Este é o plano de execução:

operador merge join_2

No plano de execução anterior, outros operadores Sort foram introduzidos pelo otimizador de consulta para alcançar as propriedades necessárias para que a mesclagem seja executada. A condição JOIN neste exemplo de consulta está somente em AlbumId, que não é como os dados são armazenados. Portanto, uma classificação precisa ser adicionada. O mecanismo de consulta é compatível com um algoritmo de Distribuição distribuída, permitindo que a classificação ocorra localmente em vez de globalmente, o que distribui e carrega em paralelo o custo da CPU.

As correspondências resultantes também podem ser filtradas por uma condição residual antes de serem retornadas. Um exemplo de onde as condições residuais aparecem é em junções de não igualdade. Os planos de execução da mesclagem podem ser complexos devido a outros requisitos de classificação. O principal algoritmo de "hash join" é adaptado para lidar com as variantes "inner", "semi", "anti" e "outer join".

Junção do hash de transmissão push

Um operador envio de hash de transmissão push é uma implementação distribuída de junções de SQL baseadas em hash. O operador de hash de transmissão push lê linhas do lado da entrada para construir um lote de dados. Em seguida, esse lote é transmitido para todos os servidores que contêm dados de mapeamento. Nos servidores de destino em que o lote de dados é recebido, uma junção de hash é criada usando o lote como os dados do lado da versão, e os dados locais são verificados como o lado da sondagem da junção de hash.

Push broadcast hash join não é selecionado automaticamente pelo otimizador. Para usar esse operador, defina o método de junção como PUSH_BROADCAST_HASH_JOIN na dica de consulta, conforme mostrado no exemplo a seguir:

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;

Estes são os resultados:

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

Este é o plano de execução:

Operador push_broadcast hash_join

A entrada da junção de hash de transmissão push é o índice AlbumsByAlbumTitle. Essa entrada é serializada em um lote de dados. Esse lote é enviado para todas as divisões locais do índice SongsBySingerAlbumSongNameDesc, em que o lote é desserializado e criado em uma tabela de hash. Em seguida, a tabela de hash usa os dados do índice local como uma sondagem que retorna correspondências resultantes.

As correspondências resultantes também podem ser filtradas por uma condição residual antes de serem retornadas. Um exemplo de onde as condições residuais aparecem é em junções de não igualdade.

Outer apply

Um operador outer apply é semelhante ao cross apply, mas em um operador "outer apply" pelo menos uma linha é retornada após a execução no lado "map" e, se necessário, uma linha preenchida com NULL é criada. Em outras palavras, fornece semântica de "left outer join".

Operadores N-ary

Um operador N-ary é um operador que tem mais de dois filhos relacionais. Os seguintes operadores são N-ary:

Union all

Um operador union all combina todos os conjuntos de linhas dos filhos sem remover duplicatas. Os operadores "union all" recebem a entrada de operadores union input distribuídos em vários servidores. O operador "union all" exige que as entradas tenham o mesmo esquema, ou seja, o mesmo conjunto de tipos de dados para cada coluna.

Por exemplo, para esta consulta:

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

O tipo de linha dos filhos consiste em dois inteiros.

Estes são os resultados:

a b
1 2
3 4
5 6

Este é o plano de execução:

union_all_operator

O operador "union all" combina as linhas de entrada e, neste exemplo, envia os resultados a um operador serialize result.

Uma consulta como a seguinte seria bem-sucedida porque o mesmo conjunto de tipos de dados é usado para cada coluna, mesmo que os filhos usem variáveis diferentes para os nomes das colunas:

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

Uma consulta como a seguinte não seria bem-sucedida porque os filhos usam tipos de dados diferentes para as colunas:

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

Subconsultas escalares

Uma subconsulta escalar é uma subexpressão SQL que faz parte de uma expressão escalar. O Spanner tenta remover subconsultas escalares sempre que possível. Em determinadas situações, no entanto, os planos podem conter explicitamente subconsultas escalares.

Por exemplo, para esta consulta:

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

Esta é a subexpressão SQL:

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

Os resultados da consulta completa são os seguintes:

FirstName
Alice 1
Catalina 0
David 0
Lea 0
Marc 0

Este é o plano de execução:

operador scalar subquery

O plano de execução contém uma subconsulta escalar, mostrada como Scalar Subquery, acima de um operador aggregate.

Às vezes, o Spanner converte subconsultas escalares em outro operador, como JOIN ou cross apply, para possivelmente melhorar o desempenho.

Por exemplo, para esta consulta:

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

Esta é a subexpressão SQL:

SELECT MAX(Duration) FROM Songs;

Os resultados da consulta completa são os seguintes:

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

Este é o plano de execução:

operador scalar subquery não aparece no plano

O plano de execução não contém uma subconsulta escalar porque o Spanner converteu a subconsulta escalar em um cross apply.

Subconsultas de matriz

Uma subconsulta de matriz é semelhante a uma subconsulta escalar, exceto que a subconsulta pode consumir mais de uma linha de entrada. As linhas consumidas são convertidas em uma única matriz de saída escalar que contém um elemento por linha de entrada consumida.

Por exemplo, para esta consulta:

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

Esta é a subconsulta:

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

Os resultados da subconsulta de cada AlbumId são convertidos em uma matriz de ConcertDate linhas em relação a AlbumId. O plano de execução contém uma subconsulta de matriz, mostrada como Array Subquery, acima de um operador "distributed union":

operador array subquery

Operadores distribuídos

Os operadores descritos anteriormente nesta página são executados dentro dos limites de uma única máquina. Operadores distribuídos são executados em vários servidores.

Os operadores a seguir são distribuídos:

O operador "distributed union" é o operador primitivo do qual se derivam o "distributed cross apply" e o "distributed outer apply".

Os operadores distribuídos aparecem em planos de execução com uma variante de distributed union acima de uma ou mais variantes de local distributed union. Uma variante de "distributed union" executa a distribuição remota de subplanos. Uma variante de "local distributed union" está acima de cada uma das verificações executadas para a consulta, conforme mostrado neste plano de execução:

operador distributed union

As variantes de "local distributed union" asseguram a execução estável da consulta quando ocorrem reinicializações para alterar dinamicamente os limites de divisão.

Sempre que possível, uma variante de "distributed union" tem um predicado de divisão que resulta em um corte da divisão, o que significa que os servidores remotos só executam subplanos nas divisões que satisfazem ao predicado. Isso melhora a latência e o desempenho geral da consulta.

Distributed union

O operador distributed union permite separar conceitualmente uma ou mais tabelas em várias divisões, avaliar remotamente uma subconsulta de modo independente em cada divisão e, em seguida, unir todos os resultados.

Por exemplo, para esta consulta:

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

Estes são os resultados:

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

Este é o plano de execução:

operador distributed union

O operador "distributed union" permite enviar subplanos aos servidores remotos que executam uma operação table scan nas divisões que atendem ao predicado WHERE s.SingerId = 2 AND s.SongGenre = 'ROCK'. Um operador serialize result calcula os valores SongName e SongGenre das linhas retornadas pelas varreduras de tabela. Em seguida, o operador "distributed union" retorna os resultados combinados dos servidores remotos como resultados da consulta SQL.

União de mesclagem distribuída

O operador distribuído união distribui uma consulta em vários servidores remotos. Em seguida, combina os resultados da consulta para produzir um resultado classificado, conhecido como classificação de mesclagem distribuída.

Uma união de mesclagem distribuída executa as seguintes etapas:

  1. O servidor raiz envia uma subconsulta para cada servidor remoto que hospeda uma divisão dos dados consultados. A subconsulta inclui instruções para que os resultados sejam classificados em uma ordem específica.

  2. Cada servidor remoto executa a subconsulta na divisão e envia os resultados de volta na ordem solicitada.

  3. O servidor raiz mescla a subconsulta classificada para produzir um resultado completamente classificado.

A união distribuída de mesclagem é ativada por padrão para o Spanner versão 3 e mais recentes.

Distributed cross apply

O operador distributed cross apply (DCA) estende o operador cross apply por meio da execução em vários servidores. O lado de entrada do DCA agrupa lotes de linhas, ao contrário de um operador de aplicação cruzada regular, que atua em apenas uma linha de entrada por vez. O lado "map" do DCA é um conjunto de operadores "cross apply" que é executado em servidores remotos.

Por exemplo, para esta consulta:

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

Os resultados vêm no formato:

AlbumTitle
Green
Nothing To Do With Me
Play
Total Junk
Green

Este é o plano de execução:

operador distributed cross apply

A entrada de DCA contém uma verificação de índice SongsBySingerAlbumSongNameDesc que agrupa linhas de AlbumId. O lado "map" desse operador "cross apply" é uma operação "index scan" no índice AlbumsByAlbumTitle, sujeito ao predicado de AlbumId na linha "input" que corresponde à chave AlbumId no índice AlbumsByAlbumTitle. O mapeamento retorna SongName para os valores SingerId nas linhas de entrada em lote.

Para resumir o processo de DCA para este exemplo, a entrada do DCA é as linhas em lote da tabela Albums, e a saída do DCA é o aplicativo dessas linhas no mapa da varredura de índice.

Distributed outer apply

O operador distributed outer apply estende o operador outer apply por meio da execução em vários servidores, semelhante ao modo em que um operador "distributed cross apply" estende um operador "cross apply".

Por exemplo, para esta consulta:

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

Os resultados vêm no formato:

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

Este é o plano de execução:

operador distributed outer apply

Apply mutations

Um operador apply mutations aplica as mutações de uma linguagem de manipulação de dados (DML, na sigla em inglês) à tabela. Ele é o operador principal em um plano de consulta para uma instrução DML.

Por exemplo, para esta consulta:

DELETE FROM Singers
WHERE FirstName = 'Alice';

Estes são os resultados:

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

Este é o plano de execução:

operador apply mutations

Mais informações

Esta seção contém os itens que não são operadores autônomos, mas que executam tarefas para aceitar um ou mais dos operadores listados acima. Os itens descritos aqui são tecnicamente operadores, mas não são operadores separados no plano de consulta.

Construtor de estrutura

Um construtor de estrutura cria uma estrutura ou uma coleção de campos. Geralmente, cria uma estrutura para linhas resultantes de uma operação de computação. Um construtor de estrutura não é um operador autônomo. Em vez disso, ele é exibido em operadores compute struct ou serialize result.

Para uma operação "compute struct", o construtor de estrutura cria uma estrutura para que as colunas das linhas computadas possam usar uma única referência de variável para a estrutura.

Para uma operação "serialize result", o construtor de estrutura cria uma estrutura para serializar os resultados.

Por exemplo, para esta consulta:

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

Estes são os resultados:

A
1

Este é o plano de execução:

construtor de estrutura

No plano de execução, os construtores de estrutura aparecem dentro de um operador "serialize result".