Operadores de execução de consulta

Introdução

Esta página descreve detalhes sobre os operadores usados nos planos de execução de consulta do Spanner. Para saber como recuperar um plano de execução para uma consulta específica usando o console do Google Cloud , consulte 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 é:

Nenhum resultado

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 de verificação:

  • 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 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. Cada subplano tem um operador serialize result e um operador de verificação de índice. 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 do 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:

Agregar

Um operador aggregate implementa instruções SQL GROUP BY 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 anterior mostra um agregado baseado em stream. Os agregados baseados em stream leem a entrada 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 instrução 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. As linhas de entrada podem ser reordenadas durante o processamento de lotes. O número de linhas de entrada que são agrupadas em cada execução do operador de lote varia.

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.

Filter

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 procura linhas que correspondem a k = 5, sem ler toda a entrada. Assim, a execução da consulta fica mais eficiente. No exemplo anterior, o operador de filtro lê apenas 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 filtro. 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ê dados somente 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

Limit

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, um operador de limite pode ser separado em um par local-global. Cada servidor remoto aplica o limite local às linhas de saída e retorna os resultados ao servidor raiz. O servidor raiz agrega as linhas enviadas pelos servidores remotos e aplica o limite global.

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.

Atribuir ID aleatório

Um operador aleatório de atribuição de ID produz saída lendo as 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 IDs aleatórios, e o operador Filter aplica um predicado escalar aos IDs 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 IDs aleatórios, e o operador Sort aplica a ordem de classificação nos IDs aleatórios e aplica LIMIT com duas linhas.

União de divisão local

Um operador local split union encontra divisões de tabela armazenadas no servidor local, executa uma subconsulta em cada divisão e cria uma união que combina todos os resultados.

Uma união de divisão local aparece em planos de execução que verificam uma tabela de posicionamento. As posições podem aumentar o número de divisões em uma tabela, tornando a verificação de divisões em lotes mais eficiente com base nos locais de armazenamento físico.

Por exemplo, suponha que a tabela Singers use uma chave de posicionamento para particionar os dados do cantor:

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

Agora, considere esta consulta:

SELECT BirthDate FROM Singers;

Este é o plano de execução:

operador local de união de divisão

A união distribuída envia uma subconsulta para cada lote de divisões armazenadas fisicamente no mesmo servidor. Em cada servidor, a união de divisão local encontra divisões que armazenam dados Singers, executa a subconsulta em cada divisão e retorna os resultados combinados. Dessa forma, a união distribuída e a união de divisão local trabalham juntas para verificar a tabela Singers com eficiência. Sem uma união de divisão local, a união distribuída enviaria uma RPC por divisão, em vez de por lote de divisão, resultando em viagens de ida e volta redundantes de RPC quando há mais de uma divisão por lote.

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:

Unspecified
[]
[[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 de ordenação com um operador LIMIT ou OFFSET é separado em um par local-global. Cada servidor remoto aplica a ordem de classificação e o limite ou 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ída 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 como entrada. 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
Rock 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ônico Azul
Rock 42
Rock 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 músicas 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 é NULL se não houver linha de mapa que corresponda a SingerId.) O operador de união distribuída na parte de cima 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
Green Let's Get Back Together
Green 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
Reproduzir Let's Get Back Together
Reproduzir 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 do build, 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".

União recursiva

Um operador de união recursiva executa uma união de duas entradas, uma que representa um caso base e a outra que representa um caso recursive. Ele é usado em consultas de gráfico com transpassagens de caminho quantificadas. A entrada de base é processada primeiro e exatamente uma vez. A entrada recursiva é processada até que a recursão seja encerrada. A recursão termina quando o limite superior, se especificado, é atingido ou quando a recursão não produz novos resultados. No exemplo a seguir, a tabela Collaborations é adicionada ao esquema, e um gráfico de propriedades chamado MusicGraph é criado.

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

A consulta de gráfico a seguir encontra cantores que colaboraram com um determinado cantor ou colaboraram com esses colaboradores.

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

operador de união recursiva

O operador de união recursiva filtra a tabela Singers para encontrar o cantor com o SingerId fornecido. Essa é a entrada de base da união recursiva. A entrada recursiva da união recursiva compreende uma aplicação cruzada distribuída ou outro operador de junção para outras consultas que unem repetidamente a tabela Collaborations aos resultados da iteração anterior da união. As linhas da entrada de base formam a etapa zero. Em cada iteração, a saída dela é armazenada pela verificação de spool recursiva. As linhas da verificação de spool recursiva são mescladas com a tabela Collaborations no spoolscan.featuredSingerId = Collaborations.SingerId. A recursão é encerrada quando duas iterações são concluídas, já que esse é o limite superior especificado na consulta.

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. No entanto, em determinados cenários, 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 Subconsulta escalar, em um operador aggregate.

O Spanner às vezes converte subconsultas escalares em outro operador, como "join" ou "cross apply", para talvez 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 "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 distributed merge union distribui uma consulta em vários servidores remotos. Em seguida, ele 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 que classificam os resultados em uma ordem específica.

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

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

A união de mesclagem distribuída é ativada por padrão para a versão 3 do Spanner e versões 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 do mapa desse operador "cross apply" é uma operação "index scan" no índice AlbumsByAlbumTitle, sujeito ao predicado de AlbumId na linha de entrada 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 instrução 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 descreve os itens que não são operadores autônomos, mas que executam tarefas para aceitar um ou mais dos operadores listados anteriormente. 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".