Operadores de ejecución de consultas

En esta página se describen los detalles sobre los operadores que se usan en los planes de ejecución de consultas de Spanner. Para saber cómo obtener un plan de ejecución de una consulta específica mediante la consola Google Cloud , consulta el artículo Cómo ejecuta Spanner las consultas.

Las consultas y los planes de ejecución de esta página se basan en el siguiente esquema de base de datos:

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

Puedes usar las siguientes instrucciones del lenguaje de manipulación de datos (DML) para añadir datos a estas tablas:

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 hojas

Un operador hoja es un operador que no tiene elementos secundarios. Los tipos de operadores de hoja son los siguientes:

Desanidación de matriz

Un operador desanidar matriz aplana una matriz de entrada en filas de elementos. Cada fila resultante contiene hasta dos columnas: el valor real de la matriz y, de forma opcional, la posición basada en cero en la matriz.

Por ejemplo, con esta consulta:

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

La consulta aplana la matriz [1,2,3] de la columna a y muestra la posición de la matriz en la columna b.

Estos son los resultados:

a b
1 0
2 1
3 2

Este es el plan de ejecución:

Operador de desanidación de arrays

Generar relación

Un operador generate relation devuelve cero o más filas.

Relación de unidades

La relación de unidad devuelve una fila. Es un caso especial del operador generate relation.

Por ejemplo, con esta consulta:

SELECT 1 + 2 AS Result;

El resultado es el siguiente:

Resultado
3

Este es el plan de ejecución:

operador de relación de unidad

Relación vacía

La relación vacía no devuelve ninguna fila. Es un caso especial del operador generate relation.

Por ejemplo, con esta consulta:

SELECT *
FROM   albums
LIMIT  0

El resultado es el siguiente:

No hay resultados

Este es el plan de ejecución:

operador de relación vacío

Examinar

Un operador scan devuelve filas analizando una fuente de filas. Estos son los tipos de operadores de búsqueda:

  • Análisis de tabla: el análisis se realiza en una tabla.
  • Análisis de índice: el análisis se realiza en un índice.
  • Análisis por lotes: el análisis se realiza en tablas intermedias creadas por otros operadores relacionales (por ejemplo, una tabla creada por un cross apply distribuido).

Siempre que sea posible, Spanner aplica predicados en las claves como parte de un análisis. Los análisis se ejecutan de forma más eficiente cuando se aplican predicados, ya que no es necesario que lean toda la tabla o el índice. Los predicados aparecen en el plan de ejecución con el formato KeyPredicate: column=value.

En el peor de los casos, una consulta puede tener que buscar todas las filas de una tabla. Esta situación lleva a un análisis completo y aparece en el plan de ejecución como full scan: true.

Por ejemplo, con esta consulta:

SELECT s.lastname
FROM   singers@{FORCE_INDEX=SingersByFirstLastName} as s
WHERE  s.firstname = 'Catalina';

Estos son los resultados:

LastName
Smith

Este es el plan de ejecución:

Operador de búsqueda

En el plan de ejecución, el operador distributed union de nivel superior envía subplanes a servidores remotos. Cada subplan tiene un operador serialize result y un operador de análisis de índice. El predicado Key Predicate: FirstName = 'Catalina' restringe la búsqueda a las filas del índice SingersByFirstLastname que tienen FirstName igual a Catalina. La salida del análisis de índice se devuelve al operador de resultados de serialización.

Operadores unarios

Un operador unario es un operador que tiene un solo elemento secundario relacional.

Los siguientes operadores son operadores unarios:

Agregar

Un operador de agregación implementa instrucciones SQL GROUP BY y funciones de agregación (como COUNT). La entrada de un operador de agregación se divide lógicamente en grupos organizados en columnas clave (o en un solo grupo si no se incluye GROUP BY). Para cada grupo, se calculan cero o más agregaciones.

Por ejemplo, con esta consulta:

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

La consulta agrupa por SingerId y realiza una agregación AVG y una agregación COUNT.

Estos son los resultados:

SingerId average recuento
3 278 1
2 225.875 8

Este es el plan de ejecución:

operador de agregación

Los operadores de agregación pueden ser basados en flujos o basados en hash. El plan de ejecución anterior muestra un agregado basado en secuencias. Los agregados basados en secuencias leen de una entrada ya preordenada (si GROUP BY está presente) y calculan el grupo sin bloquearse. Los agregados basados en hash crean tablas hash para mantener agregados incrementales de varias filas de entrada simultáneamente. Los agregados basados en secuencias son más rápidos y usan menos memoria que los agregados basados en hash, pero requieren que la entrada esté ordenada (por columnas clave o por índices secundarios).

En los casos distribuidos, un operador de agregación se puede separar en un par local-global. Cada servidor remoto realiza la agregación local en sus filas de entrada y, a continuación, devuelve los resultados al servidor raíz. El servidor raíz realiza la agregación global.

Aplicar mutaciones

Un operador apply mutations aplica las mutaciones de una declaración de manipulación de datos (DML) a la tabla. Es el operador superior de un plan de consulta de una instrucción DML.

Por ejemplo, con esta consulta:

DELETE FROM singers
WHERE  firstname = 'Alice';

Estos son los resultados:

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

Este es el plan de ejecución:

operador apply mutations

Crear lote

Un operador create batch agrupa sus filas de entrada en una secuencia. Una operación de creación por lotes suele formar parte de una operación de aplicación cruzada distribuida. Las filas de entrada se pueden reordenar durante el proceso por lotes. El número de filas de entrada que se agrupan en cada ejecución del operador de lote varía.

Consulta el operador distributed cross apply para ver un ejemplo de operador de creación de lotes en un plan de ejecución.

Computación

Un operador compute genera una salida leyendo sus filas de entrada y añadiendo una o más columnas adicionales que se calculan mediante expresiones escalares. Consulta el operador UNION ALL para ver un ejemplo de operador de cálculo en un plan de ejecución.

Estructura de Compute

Un operador compute struct crea una variable para una estructura que contiene campos para cada una de las columnas de entrada.

Por ejemplo, con 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;

Estos son los resultados:

FirstName (Nombre) Sin especificar
Alice [["Not About The Guitar","BLUES"]]

Este es el plan de ejecución:

operador de struct de cálculo

En el plan de ejecución, el operador de subconsulta de matriz recibe la entrada de un operador de unión distribuida, que a su vez recibe la entrada de un operador de estructura de cálculo. El operador de estructura de cálculo crea una estructura a partir de las columnas SongName y SongGenre de la tabla Songs.

DataBlockToRowAdapter

El optimizador de consultas de Spanner inserta automáticamente un operador DataBlockToRowAdapter entre un par de operadores que funcionan con métodos de ejecución diferentes. Su entrada es un operador que usa el método de ejecución orientado a lotes y su salida se introduce en un operador que se ejecuta con el método de ejecución orientado a filas. Para obtener más información, consulta el artículo Optimizar la ejecución de consultas.

Filtro

Un operador filter lee todas las filas de su entrada, aplica un predicado escalar en cada fila y, a continuación, devuelve solo las filas que cumplen el predicado.

Por ejemplo, con esta consulta:

SELECT s.lastname
FROM   (SELECT s.lastname
        FROM   singers AS s
        LIMIT  3) s
WHERE  s.lastname LIKE 'Rich%';

Estos son los resultados:

LastName
Richards

Este es el plan de ejecución:

operador de filtro

El predicado de los cantantes cuyo apellido empieza por Rich se implementa como un filtro. La entrada del filtro es la salida de un análisis de índice y la salida del filtro son las filas en las que LastName empieza por Rich.

Por motivos de rendimiento, siempre que un filtro se coloque directamente encima de un análisis, el filtro influye en la forma en que se leen los datos. Por ejemplo, supongamos que tenemos una tabla con la clave k. Un filtro con el predicado k = 5 directamente encima de un análisis de la tabla busca filas que coincidan con k = 5 sin leer toda la entrada. De este modo, la consulta se ejecuta de forma más eficiente. En el ejemplo anterior, el operador de filtro solo lee las filas que cumplen el predicado WHERE s.LastName LIKE 'Rich%'.

Filtrar escaneo

Un operador de análisis de filtro siempre está por encima de un análisis de tabla o índice. Funciona con el análisis para reducir el número de filas leídas de la base de datos y el análisis resultante suele ser más rápido que con un filtro. Spanner aplica el análisis de filtro en determinadas condiciones:

  • Condición de búsqueda: la condición de búsqueda se aplica si Spanner puede determinar una fila específica a la que acceder en la tabla. Por lo general, esto ocurre cuando el filtro se aplica a un prefijo de la clave principal. Por ejemplo, si la clave principal consta de Col1 y Col2, se puede buscar una cláusula WHERE que incluya valores explícitos para Col1 o Col1 y Col2. En ese caso, Spanner solo lee datos dentro del intervalo de claves.
  • Condición residual: cualquier otra condición en la que Spanner pueda evaluar el análisis para limitar la cantidad de datos que se leen.

Por ejemplo, con esta consulta:

SELECT lastname
FROM   singers
WHERE  singerid = 1

Estos son los resultados:

LastName
Richards

Este es el plan de ejecución:

operador de análisis de filtros

Límite

Un operador limit limita el número de filas devueltas. Un parámetro opcional OFFSET especifica la fila inicial que se va a devolver. En los casos distribuidos, un operador de límite se puede separar en un par local-global. Cada servidor remoto aplica el límite local a sus filas de salida y, a continuación, devuelve los resultados al servidor raíz. El servidor raíz agrega las filas enviadas por los servidores remotos y, a continuación, aplica el límite global.

Por ejemplo, con esta consulta:

SELECT s.songname
FROM   songs AS s
LIMIT  3;

Estos son los resultados:

SongName
No se trata de la guitarra
La segunda vez
Empezar de nuevo

Este es el plan de ejecución:

Operador de límite

El límite local es el límite de cada servidor remoto. El servidor raíz agrega las filas de los servidores remotos y, a continuación, aplica el límite global.

Asignación de ID aleatorio

Un operador de asignación de ID aleatorio genera resultados leyendo sus filas de entrada y añadiendo un número aleatorio a cada fila. Funciona con los operadores Filter o Sort para conseguir métodos de muestreo. Los métodos de muestreo admitidos son Bernoulli y Reservoir.

Por ejemplo, la siguiente consulta usa el muestreo de Bernoulli con una frecuencia de muestreo del 10 %.

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

Estos son los resultados:

SongName
Empezar de nuevo
Nothing Is The Same

Tenga en cuenta que, como el resultado es una muestra, puede variar cada vez que se ejecute la consulta, aunque sea la misma.

Este es el plan de ejecución:

Operador de muestreo de Bernoulli

En este plan de ejecución, el operador Random Id Assign recibe su entrada de un operador distributed union, que recibe su entrada de un index scan. El operador devuelve las filas con IDs aleatorios y, a continuación, el operador Filter aplica un predicado escalar a los IDs aleatorios y devuelve aproximadamente el 10 % de las filas.

En el siguiente ejemplo se usa el muestreo Reservoir con una frecuencia de muestreo de 2 filas.

SELECT s.songname
FROM   songs AS s TABLESAMPLE reservoir (2 rows);

Estos son los resultados:

SongName
I Knew You Were Magic
La segunda vez

Tenga en cuenta que, como el resultado es una muestra, puede variar cada vez que se ejecute la consulta, aunque sea la misma.

Este es el plan de ejecución:

operador de muestras de yacimientos

En este plan de ejecución, el operador Random Id Assign recibe su entrada de un operador distributed union, que recibe su entrada de un index scan. El operador devuelve las filas con IDs aleatorios y el operador Sort aplica el orden a los IDs aleatorios y aplica LIMIT con dos filas.

Unión de divisiones locales

Un operador local split union busca divisiones de la tabla almacenadas en el servidor local, ejecuta una subconsulta en cada división y, a continuación, crea una unión que combina todos los resultados.

Una unión de divisiones locales aparece en los planes de ejecución que analizan una tabla de emplazamientos. Las colocaciones pueden aumentar el número de divisiones de una tabla, lo que hace que sea más eficiente analizar las divisiones en lotes en función de sus ubicaciones de almacenamiento físico.

Por ejemplo, supongamos que la tabla Singers usa una clave de emplazamiento para particionar los datos de los cantantes:

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

Ahora, veamos esta consulta:

SELECT BirthDate FROM Singers;

Este es el plan de ejecución:

operador de unión dividida local

La unión distribuida envía una subconsulta a cada lote de divisiones almacenadas físicamente en el mismo servidor. En cada servidor, la unión de divisiones local busca divisiones que almacenan datos Singers, ejecuta la subconsulta en cada división y devuelve los resultados combinados. De esta forma, la unión distribuida y la unión dividida local trabajan juntas para analizar de forma eficiente la tabla Singers. Sin una unión dividida local, la unión distribuida enviaría una RPC por división en lugar de por lote de divisiones, lo que daría lugar a viajes de ida y vuelta de RPC redundantes cuando haya más de una división por lote.

RowToDataBlockAdapter

El optimizador de consultas de Spanner inserta automáticamente un operador RowToDataBlockAdapter entre un par de operadores que funcionan con métodos de ejecución diferentes. Su entrada es un operador que usa el método de ejecución orientado a filas y su salida se introduce en un operador que se ejecuta con el método de ejecución orientado a lotes. Para obtener más información, consulta el artículo Optimizar la ejecución de consultas.

Serializar resultado

Un operador serialize result es un caso especial del operador compute struct que serializa cada fila del resultado final de la consulta para devolverla al cliente.

Por ejemplo, con esta consulta:

SELECT array
  (
    select as struct so.songname,
            so.songgenre
    FROM   songs AS so
    WHERE  so.singerid = s.singerid)
FROM  singers AS s;

La consulta pide una matriz de SongName y SongGenre basada en SingerId.

Estos son los resultados:

Sin especificar
[]
[[Let's Get Back Together, COUNTRY], [Starting Again, ROCK]]
[[Not About The Guitar, BLUES]]
[]
[]

Este es el plan de ejecución:

Operador de resultados de serialización

El operador de resultados de serialización crea un resultado que contiene, para cada fila de la tabla Singers, una matriz de pares SongName y SongGenre de las canciones del cantante.

Ordenar

Un operador sort lee las filas de entrada, las ordena por columna(s) y devuelve los resultados ordenados.

Por ejemplo, con esta consulta:

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

Estos son los resultados:

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

Este es el plan de ejecución:

operador de ordenación

En este plan de ejecución, el operador de ordenación recibe sus filas de entrada de un operador de unión distribuida, ordena las filas de entrada y devuelve las filas ordenadas a un operador de serialización de resultados.

Para limitar el número de filas devueltas, un operador de ordenación puede tener opcionalmente los parámetros LIMIT y OFFSET. En los casos distribuidos, un operador de ordenación con un operador LIMIT o OFFSET se separa en un par local-global. Cada servidor remoto aplica el orden y el límite o el desplazamiento local a sus filas de entrada y, a continuación, devuelve los resultados al servidor raíz. El servidor raíz agrega las filas enviadas por los servidores remotos, las ordena y, a continuación, aplica el límite o el desplazamiento global.

Por ejemplo, con esta consulta:

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

Estos son los resultados:

SongGenre
BLUES
BLUES
BLUES

Este es el plan de ejecución:

Operador de ordenación con límite

El plan de ejecución muestra el límite local de los servidores remotos y el límite global del servidor raíz.

TVF

Un operador de función con valores de tabla genera resultados leyendo sus filas de entrada y aplicando la función especificada. La función puede implementar la asignación y devolver el mismo número de filas que la entrada. También puede ser un generador que devuelva más filas o un filtro que devuelva menos filas.

Por ejemplo, con esta consulta:

SELECT genre,
       songname
FROM   ml.predict(model genreclassifier, TABLE songs)

Estos son los resultados:

Género SongName
País No se trata de la guitarra
Rock La segunda vez
Pop Empezar de nuevo
Pop Nothing Is The Same
País Volvamos a estar juntos
Pop I Knew You Were Magic
Electrónica Azul
Rock 42
Rock Fight Story

Este es el plan de ejecución:

Operador tvf

Entrada de unión

Un operador de entrada UNION devuelve resultados a un operador UNION ALL. Consulta el operador UNION ALL para ver un ejemplo de operador de entrada de unión en un plan de ejecución.

Operadores binarios

Un operador binario es un operador que tiene dos elementos secundarios relacionales. Los siguientes operadores son operadores binarios:

Aplicación cruzada

Un operador CROSS APPLY ejecuta una consulta de tabla en cada fila extraída por una consulta de otra tabla y devuelve la unión de todas las ejecuciones de las consultas de tabla. Los operadores CROSS APPLY y OUTER APPLY ejecutan un procesamiento orientado a filas, a diferencia de los operadores que ejecutan un procesamiento basado en conjuntos, como HASH JOIN . El operador de aplicación cruzada tiene dos entradas: input y map. El operador CROSS APPLY aplica cada fila del lado de entrada al lado de asignación. El resultado de la aplicación cruzada tiene columnas tanto del lado de la entrada como del lado del mapa.

Por ejemplo, con esta consulta:

 SELECT si.firstname,
       (SELECT so.songname
        FROM   songs AS so
        WHERE  so.singerid = si.singerid
        LIMIT  1)
FROM   singers AS si;

La consulta pide el nombre de cada cantante, junto con el nombre de solo una de sus canciones.

Estos son los resultados:

FirstName (Nombre) Sin especificar
Alice No se trata de la guitarra
Catalina Volvamos a estar juntos
David NULL
Lea NULL
Marc NULL

La primera columna se rellena con datos de la tabla Singers y la segunda, con datos de la tabla Songs. En los casos en los que había un SingerId en la tabla Singers, pero no había ningún SingerId coincidente en la tabla Songs, la segunda columna contiene NULL.

Este es el plan de ejecución:

Operador de aplicación cruzada

El nodo de nivel superior es un operador de unión distribuida. El operador de unión distribuida distribuye subplanes a servidores remotos. El subplan contiene un operador serialize result que calcula el nombre del cantante y el nombre de una de sus canciones, y serializa cada fila del resultado.

El operador de resultados de serialización recibe su entrada de un operador de aplicación cruzada. El lado de entrada del operador de aplicación cruzada es un examen de la tabla Singers.

El lado del mapa de la operación de aplicación cruzada contiene lo siguiente (de arriba abajo):

  • Un operador de agregación que devuelve Songs.SongName.
  • Un operador limit que limita el número de canciones devueltas a una por cantante.
  • Un análisis del índice SongsBySingerAlbumSongNameDesc.

El operador CROSS APPLY asigna cada fila del lado de entrada a una fila del lado de asignación que tiene el mismo SingerId. El resultado del operador de aplicación cruzada es el valor FirstName de la fila de entrada y el valor SongName de la fila del mapa. El valor de SongName es NULL si no hay ninguna fila de mapa que coincida con SingerId. El operador UNION distribuido situado en la parte superior del plan de ejecución combina todas las filas de salida de los servidores remotos y las devuelve como resultados de la consulta.

Combinación hash

Un operador de combinación hash es una implementación basada en hash de las combinaciones SQL. Las combinaciones hash ejecutan el procesamiento basado en conjuntos. El operador de unión de hash lee las filas de la entrada marcada como compilación y las inserta en una tabla de hash en función de una condición de unión. A continuación, el operador de unión de hash lee las filas de la entrada marcada como sondeo. Por cada fila que lee de la entrada de la prueba, el operador de unión de hash busca filas coincidentes en la tabla de hash. El operador de unión de hash devuelve las filas coincidentes como resultado.

Por ejemplo, con 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;

Estos son los resultados:

AlbumTitle SongName
Nothing To Do With Me No se trata de la guitarra
Verde La segunda vez
Verde Empezar de nuevo
Verde Nothing Is The Same
Verde Volvamos a estar juntos
Verde I Knew You Were Magic
Verde Azul
Verde 42
Aterrorizado Fight Story

Este es el plan de ejecución:

Operador de unión hash

En el plan de ejecución, build es una unión distribuida que distribuye exploraciones en la tabla Albums. Probe es un operador de unión distribuida que distribuye análisis en el índice SongsBySingerAlbumSongNameDesc. El operador de combinación de hash lee todas las filas del lado de compilación. Cada fila de compilación se coloca en una tabla hash en función de las columnas de la condición a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId. A continuación, el operador de unión hash lee todas las filas del lado de la comprobación. Por cada fila de sondeo, el operador de unión hash busca coincidencias en la tabla hash. El operador de combinación hash devuelve las coincidencias resultantes.

Las coincidencias resultantes en la tabla hash también se pueden filtrar por una condición residual antes de que se devuelvan. (Un ejemplo de dónde aparecen las condiciones residuales es en las uniones de desigualdad). Los planes de ejecución de combinaciones hash pueden ser complejos debido a la gestión de la memoria y a las variantes de combinación. El algoritmo principal de combinación hash se adapta para gestionar las variantes de combinación interna, semi, anti y externa.

Combinación por fusión

Un operador de combinación de fusión es una implementación basada en la fusión de la combinación SQL. Ambos lados de la unión producen filas ordenadas por las columnas usadas en la condición de unión. La combinación de fusión consume ambos flujos de entrada simultáneamente y genera filas cuando se cumple la condición de combinación. Si las entradas no se ordenan originalmente como es necesario, el optimizador añade operadores Sort explícitos al plan.

El optimizador no selecciona automáticamente combinación por fusión. Para usar este operador, define el método de unión como MERGE_JOIN en la sugerencia de consulta, como se muestra en el siguiente ejemplo:

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;

Estos son los resultados:

AlbumTitle SongName
Verde La segunda vez
Verde Empezar de nuevo
Verde Nothing Is The Same
Verde Volvamos a estar juntos
Verde I Knew You Were Magic
Verde Azul
Verde 42
Aterrorizado Fight Story
Nothing To Do With Me No se trata de la guitarra

Este es el plan de ejecución:

operador de combinación por fusión_1

En este plan de ejecución, la combinación de fusión se distribuye de forma que la combinación se ejecute donde se encuentren los datos. Esto también permite que la combinación de fusión de este ejemplo funcione sin introducir operadores de ordenación adicionales, ya que ambas lecturas de tabla ya están ordenadas por SingerId y AlbumId, que es la condición de combinación. En este plan, el análisis del lado izquierdo de la tabla Albums avanza cuando su SingerId y AlbumId son comparativamente menores que el par SingerId_1 y AlbumId_1 del análisis del índice SongsBySingerAlbumSongNameDesc del lado derecho. Del mismo modo, el lado derecho avanza siempre que sea inferior al lado izquierdo. Esta fusión avanzada sigue buscando equivalencias para que se puedan devolver las coincidencias resultantes.

Veamos otro ejemplo de combinación por fusión con la siguiente consulta:

SELECT a.albumtitle,
       s.songname
FROM   albums AS a join@{join_method=merge_join} songs AS s
ON a.albumid = s.albumid;

Se obtienen los siguientes resultados:

AlbumTitle SongName
Total Junk La segunda vez
Total Junk Empezar de nuevo
Total Junk Nothing Is The Same
Total Junk Volvamos a estar juntos
Total Junk I Knew You Were Magic
Total Junk Azul
Total Junk 42
Total Junk No se trata de la guitarra
Verde La segunda vez
Verde Empezar de nuevo
Verde Nothing Is The Same
Verde Volvamos a estar juntos
Verde I Knew You Were Magic
Verde Azul
Verde 42
Verde No se trata de la guitarra
Nothing To Do With Me La segunda vez
Nothing To Do With Me Empezar de nuevo
Nothing To Do With Me Nothing Is The Same
Nothing To Do With Me Volvamos a estar juntos
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 No se trata de la guitarra
Reproducir La segunda vez
Reproducir Empezar de nuevo
Reproducir Nothing Is The Same
Reproducir Volvamos a estar juntos
Reproducir I Knew You Were Magic
Reproducir Azul
Reproducir 42
Reproducir No se trata de la guitarra
Aterrorizado Fight Story

Este es el plan de ejecución:

merge join operator_2

En el plan de ejecución anterior, el optimizador de consultas ha introducido operadores Sort adicionales para conseguir las propiedades necesarias para que se ejecute la combinación de fusión. La condición JOIN de la consulta de este ejemplo solo se aplica a AlbumId, que no es la forma en que se almacenan los datos, por lo que se debe añadir una ordenación. El motor de consultas admite un algoritmo de combinación distribuida, lo que permite que la ordenación se realice de forma local en lugar de global, lo que distribuye y paraleliza el coste de la CPU.

Las coincidencias resultantes también se pueden filtrar por una condición residual antes de que se devuelvan. (Un ejemplo de dónde aparecen las condiciones residuales es en las combinaciones de no igualdad). Los planes de ejecución de combinación de fusión pueden ser complejos debido a los requisitos de ordenación adicionales. El algoritmo principal de combinación de fusión se adapta para gestionar las variantes de combinación interna, semi, anti y externa.

Unión de hash de emisión push

Un operador de combinación hash de difusión push es una implementación distribuida basada en combinaciones hash de combinaciones SQL. El operador de combinación de difusión push lee filas del lado de entrada para crear un lote de datos. A continuación, ese lote se difunde a todos los servidores que contienen datos del lado del mapa. En los servidores de destino donde se recibe el lote de datos, se crea una combinación hash usando el lote como datos del lado de compilación y, a continuación, se analizan los datos locales como lado de sondeo de la combinación hash.

El optimizador no selecciona automáticamente Push broadcast hash join. Para usar este operador, define el método de unión como PUSH_BROADCAST_HASH_JOIN en la sugerencia de consulta, como se muestra en el siguiente ejemplo:

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;

Estos son los resultados:

AlbumTitle SongName
Verde La segunda vez
Verde Empezar de nuevo
Verde Nothing Is The Same
Verde Volvamos a estar juntos
Verde I Knew You Were Magic
Verde Azul
Verde 42
Aterrorizado Fight Story
Nothing To Do With Me No se trata de la guitarra

Este es el plan de ejecución:

Operador hash_join de push_broadcast

La entrada de la combinación hash de difusión push es el índice AlbumsByAlbumTitle. Esa entrada se serializa en un lote de datos. A continuación, ese lote se envía a todas las divisiones locales del índice SongsBySingerAlbumSongNameDesc, donde se deserializa y se convierte en una tabla hash. A continuación, la tabla hash usa los datos del índice local como una prueba que devuelve las coincidencias resultantes.

Las coincidencias resultantes también se pueden filtrar por una condición residual antes de devolverse. (Un ejemplo de dónde aparecen las condiciones residuales es en las combinaciones de no igualdad).

Outer apply

Un operador outer apply es similar a un operador cross apply, salvo que un operador outer apply asegura que cada ejecución en el mapa devuelve una fila como mínimo (e incluso produce una fila vacía, si fuera necesario). (Es decir, proporciona semántica de combinación externa izquierda).

Unión recursiva

Un operador de unión recursiva realiza una unión de dos entradas, una que representa un caso base y otra que representa un caso recursive. Se usa en consultas de gráficos con barridos de rutas cuantificados. La entrada base se procesa primero y solo una vez. La entrada recursiva se procesa hasta que finaliza la recursión. La recursión finaliza cuando se alcanza el límite superior, si se ha especificado, o cuando la recursión no produce ningún resultado nuevo. En el siguiente ejemplo, se añade la tabla Collaborations al esquema y se crea un gráfico de propiedades llamado MusicGraph.

CREATE TABLE Collaborations (
    SingerId INT64 NOT NULL,
    FeaturingSingerId INT64 NOT NULL,
    AlbumTitle STRING(MAX) NOT NULL,
) PRIMARY KEY(SingerId, FeaturingSingerId, AlbumTitle);

CREATE OR REPLACE PROPERTY GRAPH MusicGraph
    NODE TABLES(
        Singers
            KEY(SingerId)
            LABEL Singers PROPERTIES(
                BirthDate,
                FirstName,
                LastName,
                SingerId,
                SingerInfo)
            )
EDGE TABLES(
    Collaborations AS CollabWith
        KEY(SingerId, FeaturingSingerId, AlbumTitle)
        SOURCE KEY(SingerId) REFERENCES Singers(SingerId)
        DESTINATION KEY(FeaturingSingerId) REFERENCES Singers(SingerId)
        LABEL CollabWith PROPERTIES(
          AlbumTitle,
          FeaturingSingerId,
          SingerId),
);

La siguiente consulta de gráfico busca cantantes que hayan colaborado con un cantante determinado o con esos 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ón recursivo

El operador UNION RECURSIVA filtra la tabla Singers para buscar el cantante con el SingerId especificado. Esta es la entrada base de la unión recursiva. La entrada recursiva de la unión recursiva incluye un cross apply distribuido u otro operador de combinación para otras consultas que combina repetidamente la tabla Collaborations con los resultados de la iteración anterior de la combinación. Las filas del formulario de entrada base forman la iteración cero. En cada iteración, la salida se almacena mediante el análisis de cola recursivo. Las filas del análisis de carrete recursivo se combinan con la tabla Collaborations en spoolscan.featuredSingerId = Collaborations.SingerId. La recursión finaliza cuando se completan dos iteraciones, ya que ese es el límite superior especificado en la consulta.

Operadores n-arios

Un operador N-ario es un operador que tiene más de dos elementos secundarios relacionales. Los siguientes operadores son operadores N-arios:

Unión de todo

Un operador UNION ALL combina todos los conjuntos de filas de sus elementos secundarios sin eliminar los duplicados. Los operadores UNION ALL reciben su entrada de operadores UNION INPUT que se distribuyen en varios servidores. El operador UNION ALL requiere que sus entradas tengan el mismo esquema, es decir, el mismo conjunto de tipos de datos para cada columna.

Por ejemplo, con esta consulta:

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

El tipo de fila de los elementos secundarios consta de dos números enteros.

Estos son los resultados:

a b
1 2
3 4
5 6

Este es el plan de ejecución:

union_all_operator

El operador UNION ALL combina sus filas de entrada y, en este ejemplo, envía los resultados a un operador serialize result.

Una consulta como la siguiente se completaría correctamente porque se usa el mismo conjunto de tipos de datos para cada columna, aunque los elementos secundarios usen variables diferentes para los nombres de las columnas:

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

Una consulta como la siguiente no se completaría porque los elementos secundarios usan tipos de datos diferentes para las columnas:

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

Subconsultas escalares

Una subconsulta escalar es una subexpresión SQL que forma parte de una expresión escalar. Spanner intenta retirar las subconsultas escalares siempre que es posible. Sin embargo, en algunos casos, los planes pueden contener subconsultas escalares de forma explícita.

Por ejemplo, con esta consulta:

SELECT firstname,
  IF(firstname = 'Alice', (SELECT Count(*)
                          FROM   songs
                          WHERE  duration > 300), 0)
FROM   singers;

Esta es la subexpresión SQL:

SELECT Count(*)
FROM   songs
WHERE  duration > 300;

Estos son los resultados (de la consulta completa):

FirstName (Nombre)
Alice 1
Catalina 0
David 0
Lea 0
Marc 0

Este es el plan de ejecución:

operador de subconsulta escalar

El plan de ejecución contiene una subconsulta escalar, que se muestra como Subconsulta escalar, sobre un operador agregado.

A veces, Spanner convierte las subconsultas escalares en otro operador, como una unión o una aplicación cruzada, para mejorar el rendimiento.

Por ejemplo, con esta consulta:

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

Esta es la subexpresión SQL:

SELECT MAX(Duration)
FROM Songs;

Estos son los resultados (de la consulta completa):

SingerId AlbumId TrackId SongName Duración SongGenre
2 1 6 Nothing Is The Same 303 BLUES

Este es el plan de ejecución:

El operador de subconsulta escalar no se muestra en el plan

El plan de ejecución no contiene una subconsulta escalar porque Spanner ha convertido la subconsulta escalar en una aplicación cruzada.

Subconsultas de matriz

Una subconsulta de matriz es similar a una subconsulta escalar, pero la subconsulta puede usar más de una fila de entrada. Las filas que se utilizan se convierten en una matriz de salida escalar única que contiene un elemento por cada fila de entrada empleada.

Por ejemplo, con esta consulta:

SELECT a.albumid,
       array
       (
              select concertdate
              FROM   concerts
              WHERE  concerts.singerid = a.singerid)
FROM   albums AS a;

Esta es la subconsulta:

SELECT concertdate
FROM   concerts
WHERE  concerts.singerid = a.singerid;

Los resultados de la subconsulta de cada AlbumId se convierten en una matriz de filas ConcertDate con respecto a ese AlbumId. El plan de ejecución contiene una subconsulta de matriz, que se muestra como Subconsulta de matriz, encima de un operador de unión distribuida:

operador de subconsulta de matriz

Operadores distribuidos

Los operadores descritos anteriormente en esta página se ejecutan dentro de los límites de una sola máquina. Los operadores distribuidos se ejecutan en varios servidores.

Los siguientes operadores son operadores distribuidos:

El operador de unión distribuida es el operador primitivo del que se derivan las aplicaciones cruzadas distribuidas y las aplicaciones externas distribuidas.

Los operadores distribuidos aparecen en los planes de ejecución con una variante de unión distribuida encima de una o varias variantes de unión distribuida local. Una variante de unión distribuida realiza la distribución remota de los subplanes. Una variante de unión distribuida local se encuentra en la parte superior de cada uno de los análisis realizados para la consulta, tal como se muestra en este plan de ejecución:

operador distribuido

Las variantes de unión distribuida local aseguran una ejecución de consultas estable cuando se producen reinicios en los límites de división que cambian de forma dinámica.

Siempre que sea posible, una variante de unión distribuida tiene un predicado dividido que da como resultado una poda dividida, lo que significa que los servidores remotos ejecutan subplanes solo en las divisiones que cumplen el predicado. De esta forma, se mejora tanto la latencia como el rendimiento general de las consultas.

Unión distribuida

Un operador de unión distribuida divide conceptualmente una o varias tablas en varias divisiones, evalúa de forma remota una subconsulta de forma independiente en cada división y, a continuación, une todos los resultados.

Por ejemplo, con esta consulta:

SELECT s.songname,
       s.songgenre
FROM   songs AS s
WHERE  s.singerid = 2
       AND s.songgenre = 'ROCK';

Estos son los resultados:

SongName SongGenre
Empezar de nuevo ROCK
La segunda vez ROCK
Fight Story ROCK

Este es el plan de ejecución:

operador de unión distribuida

El operador UNION distribuido envía subplanes a servidores remotos, que realizan un examen de la tabla en las divisiones que cumplen el predicado WHERE s.SingerId = 2 AND s.SongGenre = 'ROCK' de la consulta. Un operador serialize result calcula los valores SongName y SongGenre de las filas devueltas por los análisis de tabla. El operador UNION distribuido devuelve los resultados combinados de los servidores remotos como resultados de la consulta SQL.

Unión de combinación distribuida

El operador unión de combinación distribuida distribuye una consulta entre varios servidores remotos. Después, combina los resultados de la consulta para generar un resultado ordenado, lo que se conoce como ordenación por fusión distribuida.

Una unión de combinación distribuida ejecuta los siguientes pasos:

  1. El servidor raíz envía una subconsulta a cada servidor remoto que aloja una división de los datos consultados. La subconsulta incluye instrucciones para que los resultados se ordenen de una forma específica.

  2. Cada servidor remoto ejecuta la subconsulta en su división y, a continuación, envía los resultados en el orden solicitado.

  3. El servidor raíz combina la subconsulta ordenada para generar un resultado completamente ordenado.

La unión de combinación distribuida está activada de forma predeterminada en la versión 3 de Spanner y versiones posteriores.

Aplicación cruzada distribuida

Un operador aplicación cruzada distribuida (DCA) amplía el operador aplicación cruzada ejecutándose en varios servidores. Los grupos de entrada de DCA lotes de filas (a diferencia de un operador CROSS APPLY normal, que actúa en una sola fila de entrada a la vez). El lado del mapa de DCA es un conjunto de operadores CROSS APPLY que se ejecutan en servidores remotos.

Por ejemplo, con esta consulta:

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

Los resultados tienen el siguiente formato:

AlbumTitle
Verde
Nothing To Do With Me
Reproducir
Total Junk
Verde

Este es el plan de ejecución:

Operador de aplicación cruzada distribuida

La entrada de DCA contiene un análisis de índice en el índice SongsBySingerAlbumSongNameDesc que agrupa filas de AlbumId. El lado de mapa de este operador de aplicación cruzada es un análisis de índice en el índice AlbumsByAlbumTitle, sujeto al predicado de AlbumId en la fila de entrada que coincida con la clave AlbumId en el índice AlbumsByAlbumTitle. La asignación devuelve el SongName de los valores de SingerId en las filas de entrada por lotes.

Para resumir el proceso de DCA en este ejemplo, la entrada del DCA son las filas agrupadas de la tabla Albums y la salida del DCA es la aplicación de estas filas al mapa del análisis de índice.

Aplicación externa distribuida

Un operador OUTER APPLY distribuido amplía el operador OUTER APPLY ejecutándose en varios servidores, de forma similar a como un operador CROSS APPLY distribuido amplía un operador CROSS APPLY.

Por ejemplo, con esta consulta:

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

Los resultados tienen el siguiente 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 es el plan de ejecución:

Operador de aplicación externa distribuido

Aplicar mutaciones

Un operador apply mutations aplica las mutaciones de una declaración de manipulación de datos (DML) a la tabla. Es el operador superior de un plan de consulta de una instrucción DML.

Por ejemplo, con esta consulta:

DELETE FROM singers
WHERE  firstname = 'Alice';

Estos son los resultados:

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

Este es el plan de ejecución:

operador apply mutations

Información adicional

En esta sección se describen los elementos que no son operadores independientes, sino que ejecutan tareas para admitir uno o varios de los operadores que se han indicado anteriormente. Los elementos que se describen aquí son técnicamente operadores, pero no son operadores independientes en tu plan de consulta.

Constructor de struct

Un constructor de struct crea un struct, o una colección de campos. Normalmente, crea una struct para las filas que resultan de una operación de cálculo. Un constructor de struct no es un operador independiente. En su lugar, aparece en los operadores compute struct u serialize result.

En el caso de una operación de estructura de cálculo, el constructor de la estructura crea una estructura para que las columnas de las filas calculadas puedan usar una sola referencia de variable a la estructura.

En el caso de una operación de serialización de resultados, el constructor de struct crea un struct para serializar los resultados.

Por ejemplo, con esta consulta:

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

Estos son los resultados:

A
1

Este es el plan de ejecución:

constructor de struct

En el plan de ejecución, los constructores de struct aparecen dentro de un operador de resultado de serialización.