Operadores de execução de consultas

Esta página descreve detalhes sobre os operadores usados nos planos de execução de consultas do Spanner. Para saber como obter um plano de execução para uma consulta específica através da consola Google Cloud , consulte o artigo Compreender como o Spanner executa consultas.

As consultas e os planos de execução nesta página baseiam-se no seguinte esquema da base de dados:

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

Pode usar as seguintes declaraçõ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 de folhas

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

Desaninhamento de matriz

Um operador array unnest transforma uma matriz de entrada em linhas de elementos. Cada linha resultante contém até duas colunas: o valor real da matriz e, opcionalmente, a posição baseada em zero na matriz.

Por exemplo, usando esta consulta:

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

A consulta reduz 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 de anulação da aninhagem de matrizes

Gere a relação

Um operador generate relation devolve zero ou mais linhas.

Relação de unidades

A relação de unidade devolve uma linha. É um caso especial do operador generate relation.

Por exemplo, usando esta consulta:

SELECT 1 + 2 AS Result;

O resultado é:

Resultado
3

Este é o plano de execução:

operador de relação de unidades

Relação vazia

A relação vazia não devolve linhas. É um caso especial do operador generate relation.

Por exemplo, usando esta consulta:

SELECT *
FROM   albums
LIMIT  0

O resultado é:

Nenhum resultado

Este é o plano de execução:

operador de relação vazio

A digitalizar

Um operador scan devolve linhas ao analisar uma origem de linhas. Estes são os tipos de operadores de procura:

  • Leitura de mesa: a leitura ocorre numa mesa.
  • Análise de índice: a análise ocorre num índice.
  • Análise em lote: a análise ocorre em tabelas intermédias criadas por outros operadores relacionais (por exemplo, uma tabela criada por uma aplicação cruzada distribuída).

Sempre que possível, o Spanner aplica predicados em chaves como parte de uma análise. As análises são executadas de forma mais eficiente quando são aplicados predicados, porque a análise não precisa de ler a tabela ou o índice completo. Os predicados aparecem no plano de execução no formato KeyPredicate: column=value.

No pior caso, uma consulta pode ter de procurar todas as linhas numa tabela. Esta situação leva a uma análise completa e aparece no plano de execução como full scan: true.

Por exemplo, usando 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 de procura

No plano de execução, o operador distributed union de nível superior envia subplanos para servidores remotos. Cada subplano tem um operador serialize result e um operador de análise de índice. O predicado Key Predicate: FirstName = 'Catalina'restringe a análise às linhas no índiceSingersByFirstLastname que têm FirstName igual a Catalina. O resultado da análise de índice é devolvido ao operador de resultado de serialização.

Operadores unários

Um operador unário é um operador que tem um único filho relacional.

Os seguintes operadores são operadores unários:

Agregar

Um operador aggregate implementa declarações SQL GROUP BY e funções aggregate (como COUNT). A entrada para um operador aggregate é logicamente particionada em grupos organizados em colunas de chaves (ou num único grupo se GROUP BY não estiver presente). Para cada grupo, são calculados zero ou mais agregados.

Por exemplo, usando esta consulta:

SELECT s.singerid,
       Avg(s.duration) AS average,
       Count(*)        AS count
FROM   songs AS s
GROUP  BY singerid;

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

Estes são os resultados:

SingerId média contagem
3 278 1
2 225,875 8

Este é o plano de execução:

operador de agregação

Os operadores de agregação podem ser baseados em streams ou baseados em hash. O plano de execução anterior mostra um agregado baseado em streams. Os agregados baseados em streams leem a entrada já pré-ordenada (se GROUP BY estiver presente) e calculam o grupo sem bloqueio. Os dados agregados baseados em hash criam tabelas de hash para manter os dados agregados incrementais de várias linhas de entrada em simultâneo. Os agregados baseados em streams são mais rápidos e usam menos memória do que os agregados baseados em hash, mas requerem que a entrada seja ordenada (por colunas-chave ou índices secundários).

Para cenários distribuídos, um operador agregado pode ser separado num par local-global. Cada servidor remoto executa a agregação local nas respetivas linhas de entrada e, em seguida, devolve os resultados ao servidor raiz. O servidor de raiz realiza a agregação global.

Aplique mutações

Um operador apply mutations aplica as mutações de uma declaração de manipulação de dados (DML) à tabela. É o operador principal num plano de consulta para uma declaração DML.

Por exemplo, usando 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

Criar lote

Um operador create batch agrupa as linhas de entrada numa sequência. Normalmente, uma operação de criação em lote ocorre como parte de uma operação de aplicação cruzada distribuída. As linhas de entrada podem ser reordenadas durante o processamento em lote. O número de linhas de entrada que são processadas em lote em cada execução do operador de lote varia.

Consulte o operador distributed cross apply para ver um exemplo de um operador de criação de lotes num plano de execução.

Computação

Um operador de cálculo produz resultados lendo as respetivas linhas de entrada e adicionando uma ou mais colunas adicionais que são calculadas através de expressões escalares. Consulte o operador union all para ver um exemplo de um operador de cálculo num plano de execução.

Estrutura de computação

Um operador compute struct cria uma variável para uma estrutura que contém campos para cada uma das colunas de entrada.

Por exemplo, usando 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 Não especificado
Alice [["Not About The Guitar","BLUES"]]

Este é o plano de execução:

compute struct operator

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

DataBlockToRowAdapter

Um operador DataBlockToRowAdapter é inserido automaticamente pelo otimizador de consultas do Spanner entre um par de operadores que funcionam com métodos de execução diferentes. A sua entrada é um operador que usa o método de execução orientado por lotes e a sua saída é introduzida num operador que executa o método de execução orientado por linhas. Para mais informações, consulte o artigo Otimize a execução de consultas.

Filtro

Um operador de filtro lê todas as linhas da respetiva entrada, aplica um predicado escalar em cada linha e, em seguida, devolve apenas as linhas que satisfazem o predicado.

Por exemplo, usando 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 de filtro

O predicado para cantores cujo apelido começa por Rich é implementado como um filtro. A entrada do filtro é a saída de uma análise de índice e a saída do filtro são linhas em que LastName começa por Rich.

Para o desempenho, sempre que um filtro estiver posicionado diretamente acima de uma análise, o filtro afeta a forma como os dados são lidos. Por exemplo, considere uma tabela com a chave k. Um filtro com predicado k = 5 diretamente sobre uma análise da tabela procura linhas que correspondam a k = 5, sem ler a entrada completa. Isto resulta numa execução mais eficiente da consulta. No exemplo anterior, o operador de filtro lê apenas as linhas que satisfazem o predicado WHERE s.LastName LIKE 'Rich%'.

Digitalização de filtros

Um operador de análise de filtro está sempre acima de uma análise de tabela ou de índice. Funciona com a análise para reduzir o número de linhas lidas da base de dados e a análise resultante é normalmente mais rápida do que com um filtro. O Spanner aplica a análise de filtros em determinadas condições:

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

Por exemplo, usando esta consulta:

SELECT lastname
FROM   singers
WHERE  singerid = 1

Estes são os resultados:

LastName
Richards

Este é o plano de execução:

operador de análise de filtros

Limite

Um operador limit restringe o número de linhas devolvidas. Um parâmetro OFFSET opcional especifica a linha inicial a devolver. Para cenários distribuídos, um operador de limite pode ser separado num par local-global. Cada servidor remoto aplica o limite local às respetivas linhas de saída e, em seguida, devolve os resultados ao servidor raiz. O servidor raiz agrega as linhas enviadas pelos servidores remotos e, em seguida, aplica o limite global.

Por exemplo, usando esta consulta:

SELECT s.songname
FROM   songs AS s
LIMIT  3;

Estes são os resultados:

SongName
Not About The Guitar
A segunda vez
Recomeçar

Este é o plano de execução:

operador de limite

O limite local é o limite para cada servidor remoto. O servidor raiz agrega as linhas dos servidores remotos e, em seguida, aplica o limite global.

Atribuição de ID aleatório

Um operador de atribuição de ID aleatório produz resultados lendo as respetivas linhas de entrada e adicionando um número aleatório a cada linha. Funciona com um operador Filter ou Sort para alcançar métodos de amostragem. Os métodos de amostragem suportados são Bernoulli e Reservoir.

Por exemplo, a seguinte consulta usa a amostragem de Bernoulli com uma taxa de amostragem de 10 por cento.

SELECT s.songname
FROM   songs AS s TABLESAMPLE bernoulli (10 PERCENT);

Estes são os resultados:

SongName
Recomeçar
Nothing Is The Same

Tenha em atenção que, uma vez que o resultado é uma amostra, pode variar cada vez que a consulta é executada, mesmo que seja a mesma.

Este é o plano de execução:

operador de exemplo de Bernoulli

Neste plano de execução, o operador Random Id Assign recebe a respetiva entrada de um operador de união distribuída, que recebe a respetiva entrada de uma análise de índice. O operador devolve as linhas com IDs aleatórios e, em seguida, o operador Filter aplica um predicado escalar aos IDs aleatórios e devolve aproximadamente 10% das linhas.

O exemplo seguinte 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
A segunda vez

Tenha em atenção que, uma vez que o resultado é uma amostra, pode variar cada vez que a consulta é executada, mesmo que 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 a respetiva entrada de um operador de união distribuída, que recebe a respetiva entrada de uma análise de índice. O operador devolve as linhas com IDs aleatórios e, em seguida, o operador Sort aplica a ordem de ordenação aos IDs aleatórios e aplica LIMIT com 2 linhas.

Local split union

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

Uma união de divisão local aparece em planos de execução que analisam uma tabela de posicionamento. Os posicionamentos podem aumentar o número de divisões numa tabela, tornando mais eficiente a análise de divisões em lotes com base nas respetivas localizações de armazenamento físico.

Por exemplo, suponhamos que a tabela Singers usa uma chave de posicionamento para particionar os dados de cantores:

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 de união de divisão local

A união distribuída envia uma subconsulta a cada lote de divisões armazenadas fisicamente em conjunto 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 devolve os resultados combinados. Desta forma, a união distribuída e a união dividida local funcionam em conjunto para analisar de forma eficiente a tabela Singers. Sem uma união dividida local, a união distribuída enviaria um RPC por divisão, em vez de por lote de divisão, o que resultaria em viagens de ida e volta de RPC redundantes quando existe mais do que uma divisão por lote.

RowToDataBlockAdapter

Um operador RowToDataBlockAdapter é inserido automaticamente pelo otimizador de consultas do Spanner entre um par de operadores que funcionam com métodos de execução diferentes. A sua entrada é um operador que usa o método de execução orientado por linhas e a sua saída é introduzida num operador que executa o método de execução orientado por lotes. Para mais informações, consulte o artigo Otimize a execução de consultas.

Serializar resultado

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

Por exemplo, usando 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 de resultados de serialização

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

Ordenar

Um operador sort lê as linhas de entrada, ordena-as por colunas e, em seguida, devolve os resultados ordenados.

Por exemplo, usando esta consulta:

SELECT s.songgenre
FROM   songs AS s
ORDER  BY songgenre;

Estes são os resultados:

SongGenre
BLUES
BLUES
BLUES
BLUES
CLÁSSICA
PAÍS
ROCK
ROCK
ROCK

Este é o plano de execução:

operador de ordenação

Neste plano de execução, o operador de ordenação recebe as respetivas linhas de entrada de um operador de união distribuída, ordena as linhas de entrada e devolve as linhas ordenadas a um operador de serialização de resultados.

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

Por exemplo, usando 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 de ordenação com limite

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

TVF

Um operador de função de valor de tabela produz resultados lendo as respetivas linhas de entrada e aplicando a função especificada. A função pode implementar o mapeamento e devolver o mesmo número de linhas que a entrada. Também pode ser um gerador que devolve mais linhas ou um filtro que devolve menos linhas.

Por exemplo, usando 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 A segunda vez
Pop Recomeçar
Pop Nothing Is The Same
País Vamos voltar a juntar-nos
Pop I Knew You Were Magic
Eletrónica Azul
Rock 42
Rock História de luta

Este é o plano de execução:

tvf operator

Entrada da união

Um operador union input devolve resultados a um operador union all. Consulte o operador union all para ver um exemplo de um operador de entrada de união num plano de execução.

Operadores binários

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

Aplicação cruzada

Um operador cross apply executa uma consulta de tabela em cada linha obtida por uma consulta de outra tabela e devolve a união de todas as execuções de consultas de tabelas. Os operadores de aplicação cruzada e aplicação externa executam o processamento orientado por linhas, ao contrário dos operadores que executam o processamento baseado em conjuntos, como a junção hash . O operador de aplicação cruzada tem duas entradas: input e map. O operador cross apply aplica cada linha do lado de entrada ao lado de mapeamento. O resultado da aplicação cruzada tem colunas dos lados de entrada e de mapeamento.

Por exemplo, usando 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 próprio de cada cantor, juntamente com o nome de apenas uma das músicas do cantor.

Estes são os resultados:

FirstName Não especificado
Alice Not About The Guitar
Catalina Vamos voltar a juntar-nos
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. Nos casos em que existia um SingerId na tabela Singers, mas não existia nenhum SingerId correspondente na tabela Songs, a segunda coluna contém NULL.

Este é o plano de execução:

operador de aplicação cruzada

O nó de nível superior é um operador de união distribuída. O operador de união distribuída distribui subplanos para servidores remotos. O subplano contém um operador serialize result que calcula o nome próprio do cantor e o nome de uma das músicas do cantor e serializa cada linha do resultado.

O operador de resultado de serialização recebe a respetiva entrada de um operador de aplicação cruzada. O lado de entrada do operador cross apply é uma análise da tabela Singers.

O lado do mapa para a operação de aplicação cruzada contém o seguinte (de cima para baixo):

  • Um operador agregado que devolve Songs.SongName.
  • Um operador limit que limita o número de músicas devolvidas a uma por cantor.
  • Uma análise de índice no índice SongsBySingerAlbumSongNameDesc.

O operador de aplicação cruzada mapeia cada linha do lado de entrada para uma linha do lado de mapeamento que tenha o mesmo SingerId. O resultado do operador de aplicação cruzada é o valor FirstName da linha de entrada e o valor SongName da linha de mapeamento. (O valor SongName é NULL se não existir uma linha de mapeamento com correspondência em 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 devolve-as como os resultados da consulta.

Junção hash

Um operador hash join é uma implementação baseada em hash de junções SQL. As junções hash executam o processamento baseado em conjuntos. O operador de junção hash lê linhas da entrada marcada como build e insere-as numa tabela hash com base numa condição de junção. Em seguida, o operador de junção hash lê as linhas da entrada marcada como sonda. Para cada linha lida a partir da entrada da sonda, o operador de junção hash procura linhas correspondentes na tabela hash. O operador de junção hash devolve as linhas correspondentes como resultado.

Por exemplo, usando 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
Nada a ver comigo Not About The Guitar
Verde A segunda vez
Verde Recomeçar
Verde Nothing Is The Same
Verde Vamos voltar a juntar-nos
Verde I Knew You Were Magic
Verde Azul
Verde 42
Aterrorizado História de luta

Este é o plano de execução:

operador de junção hash

No plano de execução, build é uma união distribuída que distribui análises na tabela Albums. Probe é um operador de união distribuído que distribui as análises no índice SongsBySingerAlbumSongNameDesc. O operador de junção hash lê todas as linhas do lado de criação. Cada linha de compilação é colocada numa tabela de hash com base nas colunas na condição a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId. Em seguida, o operador de junção hash lê todas as linhas do lado de sondagem. Para cada linha de sondagem, o operador de junção hash procura correspondências na tabela hash. As correspondências resultantes são devolvidas pelo operador de junção hash.

As correspondências resultantes na tabela de hash também podem ser filtradas por uma condição residual antes de serem devolvidas. (Um exemplo de onde aparecem condições residuais é em junções de não igualdade). Os planos de execução de junções hash podem ser complexos devido à gestão de memória e às variantes de junção. O algoritmo de junção hash principal é adaptado para processar variantes de junção interna, semi, anti e externa.

União de intercalação

Um operador de junção de união é uma implementação baseada na união da junção SQL. Ambos os lados da junção produzem linhas ordenadas pelas colunas usadas na condição de junção. A junção de união consome ambas as streams de entrada em simultâneo e gera linhas quando a condição de junção é satisfeita. Se as entradas não estiverem originalmente ordenadas conforme necessário, o otimizador adiciona operadores Sort explícitos ao plano.

A junção de união não é selecionada automaticamente pelo otimizador. Para usar este operador, defina o método de junção como MERGE_JOIN na sugestão de consulta, conforme mostrado no exemplo seguinte:

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 A segunda vez
Verde Recomeçar
Verde Nothing Is The Same
Verde Vamos voltar a juntar-nos
Verde I Knew You Were Magic
Verde Azul
Verde 42
Aterrorizado História de luta
Nada a ver comigo Not About The Guitar

Este é o plano de execução:

merge join operator_1

Neste plano de execução, a junção de intercalação é distribuída para que a junção seja executada onde os dados estão localizados. Isto também permite que a junção de união neste exemplo funcione sem a introdução de operadores de ordenação adicionais, porque as análises de tabelas já estão ordenadas por SingerId, AlbumId, que é a condição de junção. Neste plano, a leitura do lado esquerdo da tabela Albums avança sempre que o par SingerId, AlbumId for comparativamente inferior ao par SongsBySingerAlbumSongNameDesc, SingerId_1 do índice de leitura do lado direito.AlbumId_1 Da mesma forma, o lado direito avança sempre que for inferior ao lado esquerdo. Esta união avançada continua a pesquisar equivalências para que possam ser devolvidas correspondências resultantes.

Considere outro exemplo de junção de união com 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;

Produz os seguintes resultados:

AlbumTitle SongName
Total de lixo A segunda vez
Total de lixo Recomeçar
Total de lixo Nothing Is The Same
Total de lixo Vamos voltar a juntar-nos
Total de lixo I Knew You Were Magic
Total de lixo Azul
Total de lixo 42
Total de lixo Not About The Guitar
Verde A segunda vez
Verde Recomeçar
Verde Nothing Is The Same
Verde Vamos voltar a juntar-nos
Verde I Knew You Were Magic
Verde Azul
Verde 42
Verde Not About The Guitar
Nada a ver comigo A segunda vez
Nada a ver comigo Recomeçar
Nada a ver comigo Nothing Is The Same
Nada a ver comigo Vamos voltar a juntar-nos
Nada a ver comigo I Knew You Were Magic
Nada a ver comigo Azul
Nada a ver comigo 42
Nada a ver comigo Not About The Guitar
Reproduzir A segunda vez
Reproduzir Recomeçar
Reproduzir Nothing Is The Same
Reproduzir Vamos voltar a juntar-nos
Reproduzir I Knew You Were Magic
Reproduzir Azul
Reproduzir 42
Reproduzir Not About The Guitar
Aterrorizado História de luta

Este é o plano de execução:

merge join operator_2

No plano de execução anterior, o otimizador de consultas introduziu operadores Sort adicionais para alcançar as propriedades necessárias para a execução da junção de união. A condição JOIN na consulta deste exemplo é apenas em AlbumId, que não é a forma como os dados são armazenados, pelo que tem de ser adicionada uma ordenação. O motor de consulta suporta um algoritmo de união distribuída, o que permite que a ordenação ocorra localmente em vez de globalmente, o que distribui e paraleliza o custo da CPU.

As correspondências resultantes também podem ser filtradas por uma condição residual antes de serem devolvidas. (Um exemplo de onde aparecem condições residuais é em junções de não igualdade). Os planos de execução de junções de união podem ser complexos devido a requisitos de ordenação adicionais. O algoritmo de junção de união principal é adaptado para processar variantes de junção interna, semi, anti e externa.

Junção de hash de transmissão de envio

Um operador push broadcast hash join é uma implementação distribuída baseada em hash-join de junções SQL. O operador de junção hash de transmissão push lê linhas do lado de entrada para construir um lote de dados. Em seguida, esse lote é transmitido a todos os servidores que contêm dados do lado do mapa. Nos servidores de destino onde o lote de dados é recebido, é criada uma associação hash com o lote como os dados do lado de criação e, em seguida, os dados locais são analisados como o lado de sondagem da associação hash.

A junção de hash de transmissão push não é selecionada automaticamente pelo otimizador. Para usar este operador, defina o método de junção como PUSH_BROADCAST_HASH_JOIN na dica de consulta, conforme mostrado no exemplo seguinte:

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 A segunda vez
Verde Recomeçar
Verde Nothing Is The Same
Verde Lets Get Back Together
Verde I Knew You Were Magic
Verde Azul
Verde 42
Aterrorizado História de luta
Nada a ver comigo Not About The Guitar

Este é o plano de execução:

push_broadcast hash_join operator

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

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

Aplicação externa

Um operador outer apply é semelhante a um operador cross apply, exceto que um operador outer apply garante que cada execução no lado do mapa devolve, pelo menos, uma linha, criando uma linha com preenchimento NULL, se necessário. (Por outras palavras, fornece semântica de junção externa esquerda.)

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. É usado em consultas de gráficos com travessias de caminhos quantificadas. A entrada base é processada primeiro e exatamente uma vez. A entrada recursiva é processada até a recursão terminar. A recursão termina quando o limite superior, se especificado, for atingido ou quando a recursão não produzir novos resultados. No exemplo seguinte, a tabela Collaborations é adicionada ao esquema e é criado um gráfico de propriedades denominado 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),
);

A seguinte consulta de grafos encontra cantores que colaboraram com um determinado cantor ou que 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 recursivo

O operador recursive union filtra a tabela Singers para encontrar o cantor com o SingerId especificado. Esta é a entrada base para a união recursiva. A entrada recursiva para a união recursiva compreende uma aplicação cruzada distribuída ou outro operador de junção para outras consultas que junta repetidamente a tabela Collaborations com os resultados da iteração anterior da junção. As linhas da entrada base formam a iteração zero. Em cada iteração, a saída da iteração é armazenada pela análise recursiva de spool. As linhas da análise de spool recursiva são unidas com a tabela Collaborations em spoolscan.featuredSingerId = Collaborations.SingerId. A recursão termina quando duas iterações estão concluídas, uma vez que esse é o limite superior especificado na consulta.

Operadores de n-aridade

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

União de tudo

Um operador union all combina todos os conjuntos de linhas dos respetivos elementos secundários sem remover duplicados. Todos os operadores de união recebem a respetiva entrada de operadores union input que são distribuídos por vários servidores. O operador union all requer que as respetivas entradas tenham o mesmo esquema, ou seja, o mesmo conjunto de tipos de dados para cada coluna.

Por exemplo, usando 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 para os elementos secundários consiste em dois números 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 respetivas linhas de entrada e, neste exemplo, envia os resultados para um operador serialize result.

Uma consulta como a seguinte teria êxito, porque é usado o mesmo conjunto de tipos de dados para cada coluna, mesmo que os elementos secundários 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 elementos subordinados 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, usando 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;

Estes são os resultados (da consulta completa):

FirstName
Alice 1
Catalina 0
David 0
Lea 0
Marc 0

Este é o plano de execução:

operador de subconsulta escalar

O plano de execução contém uma subconsulta escalar, apresentada como Subconsulta escalar, sobre um operador aggregate.

Por vezes, o Spanner converte subconsultas escalares noutro operador, como uma junção ou uma aplicação cruzada, para possivelmente melhorar o desempenho.

Por exemplo, usando esta consulta:

SELECT *
FROM   songs
WHERE  duration = (SELECT Max(duration)
                   FROM   songs);

Esta é a subexpressão SQL:

SELECT MAX(Duration)
FROM Songs;

Estes são os resultados (da consulta completa):

SingerId AlbumId TrackId SongName Duração SongGenre
2 1 6 Nothing Is The Same 303 BLUES

Este é o plano de execução:

O operador de subconsulta escalar não é apresentado no plano

O plano de execução não contém uma subconsulta escalar porque o Spanner converteu a subconsulta escalar numa aplicação cruzada.

Subconsultas de matrizes

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

Por exemplo, usando 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 para cada AlbumId são convertidos numa matriz de linhas em função desse AlbumId.ConcertDate O plano de execução contém uma subconsulta de matriz, apresentada como Subconsulta de matriz, acima de um operador de união distribuído:

operador de subconsulta de matriz

Operadores distribuídos

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

Os seguintes operadores são operadores distribuídos:

O operador de união distribuído é o operador primitivo a partir do qual são derivados o cross apply distribuído e o outer apply distribuído.

Os operadores distribuídos aparecem nos planos de execução com uma variante de união distribuída no topo de uma ou mais variantes de união distribuída local. Uma variante de união distribuída executa a distribuição remota de subplanos. Uma variante de união distribuída local está no topo de cada uma das análises realizadas para a consulta, conforme mostrado neste plano de execução:

operador distribuído

As variantes de união distribuídas locais garantem uma execução de consultas estável quando ocorrem reinícios para limites de divisão que mudam dinamicamente.

Sempre que possível, uma variante de união distribuída tem um predicado dividido que resulta na eliminação de divisões, o que significa que os servidores remotos executam subplanos apenas nas divisões que satisfazem o predicado. Isto melhora a latência e o desempenho geral das consultas.

União distribuída

Um operador de união distribuída divide conceptualmente uma ou mais tabelas em várias divisões, avalia remotamente uma subconsulta de forma independente em cada divisão e, em seguida, une todos os resultados.

Por exemplo, usando 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
Recomeçar ROCK
A segunda vez ROCK
História de luta ROCK

Este é o plano de execução:

operador de união distribuída

O operador de união distribuída envia subplanos para servidores remotos, que executam uma análise de tabelas em divisões que satisfazem o predicado da consulta WHERE s.SingerId = 2 AND s.SongGenre = 'ROCK'. Um operador serialize result calcula os valores SongName e SongGenre a partir das linhas devolvidas pelas análises de tabelas. O operador de união distribuída devolve os resultados combinados dos servidores remotos como os resultados da consulta SQL.

União de intercalação distribuída

O operador distributed merge union distribui uma consulta por vários servidores remotos. Em seguida, combina os resultados da consulta para produzir um resultado ordenado, conhecido como ordenação por intercalação distribuída.

Uma união de união distribuída executa os seguintes passos:

  1. O servidor raiz envia uma subconsulta a cada servidor remoto que aloja uma divisão dos dados consultados. A subconsulta inclui instruções que os resultados são ordenados por uma ordem específica.

  2. Cada servidor remoto executa a subconsulta na respetiva divisão e, em seguida, envia os resultados na ordem pedida.

  3. O servidor raiz une a subconsulta ordenada para produzir um resultado completamente ordenado.

A união de união distribuída está ativada por predefinição para a versão 3 e posteriores do Spanner.

Aplicação cruzada distribuída

Um operador de aplicação cruzada distribuída (DCA) expande o operador de aplicação cruzada através 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 normal, que atua apenas numa linha de entrada de cada vez). O lado do mapa da DCA é um conjunto de operadores de aplicação cruzada que são executados em servidores remotos.

Por exemplo, usando esta consulta:

SELECT albumtitle
FROM   songs
       JOIN albums
         ON albums.albumid = songs.albumid;

Os resultados estão no formato:

AlbumTitle
Verde
Nada a ver comigo
Reproduzir
Total de lixo
Verde

Este é o plano de execução:

operador de aplicação cruzada distribuído

A entrada de DCA contém um índice scan no índice SongsBySingerAlbumSongNameDesc que agrupa linhas de AlbumId. O lado do mapa para este operador de aplicação cruzada é uma análise de índice no índice AlbumsByAlbumTitle, sujeito ao predicado de AlbumId na linha de entrada que corresponde à chave AlbumId no índice AlbumsByAlbumTitle. O mapeamento devolve o SongName para os valores SingerId nas linhas de entrada em lote.

Para resumir o processo de DCA neste exemplo, a entrada do DCA são as linhas em lote da tabela Albums e a saída do DCA é a aplicação destas linhas ao mapa da leitura do índice.

Aplicação externa distribuída

Um operador distributed outer apply expande o operador outer apply através da execução em vários servidores, de forma semelhante à forma como um operador distributed cross apply expande um operador cross apply.

Por exemplo, usando esta consulta:

SELECT lastname,
       concertdate
FROM   singers LEFT OUTER join@{JOIN_TYPE=APPLY_JOIN} concerts
ON singers.singerid=concerts.singerid;

Os resultados estão 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 de aplicação externa distribuído

Aplique mutações

Um operador apply mutations aplica as mutações de uma declaração de manipulação de dados (DML) à tabela. É o operador principal num plano de consulta para uma declaração DML.

Por exemplo, usando 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

Informações adicionais

Esta secção descreve itens que não são operadores autónomos, mas que, em vez disso, executam tarefas para suportar um ou mais dos operadores listados anteriormente. Os itens descritos aqui são tecnicamente operadores, mas não são operadores separados no seu plano de consulta.

Construtor de struct

Um construtor de struct cria um struct ou uma coleção de campos. Normalmente, cria uma struct para linhas resultantes de uma operação de cálculo. Um construtor de struct não é um operador autónomo. Em alternativa, aparece nos operadores compute struct ou nos operadores serialize result.

Para uma operação de estrutura de cálculo, o construtor de estruturas cria uma estrutura para que as colunas das linhas calculadas possam usar uma única referência de variável à estrutura.

Para uma operação de resultado de serialização, o construtor de struct cria um struct para serializar os resultados.

Por exemplo, usando 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 struct

No plano de execução, os construtores de estruturas aparecem dentro de um operador de resultado de serialização.