Autounirse en clave primaria

9

Considere esta consulta que consiste Nen autouniones:

select
    t1.*
from [Table] as t1
join [Table] as t2 on
    t1.Id = t2.Id
-- ...
join [Table] as tN on
    t1.Id = tN.Id

Produce un plan de ejecución con N escaneos de índice agrupados y uniones de fusión N-1.

Honestamente, no veo ninguna razón para no optimizar todas las uniones y hacer solo un escaneo de índice agrupado, es decir, optimizar la consulta original para esto:

select
    t1.*
from [Table] as t1

Preguntas

  • ¿Por qué las uniones no están optimizadas?
  • ¿Es matemáticamente incorrecto decir que cada combinación no cambia el conjunto de resultados?

Probado en:

  • Versión del servidor de origen: SQL Server 2014 (12.0.4213)
  • Fuente Motor de base de datos Edición: Microsoft SQL Server Standard Edition
  • Tipo de motor de base de datos de origen: SQL Server independiente
  • Nivel de compatibilidad: SQL Server 2008 (100)

La consulta no tiene sentido; simplemente vino a mi mente y tengo curiosidad por eso ahora.

Aquí está el violín con la creación de tablas y 3 consultas: con inner join's, con left join' sy mezclado. También puede ver el plan de ejecución allí también.

Parece que los left joins se eliminan en el plan de ejecución de resultados mientras que los inner joins no. Sin embargo, todavía no entiendo por qué .

pkuderov
fuente

Respuestas:

18

Primero, supongamos que esa (id)es la clave principal de la tabla. En este caso, sí, las uniones son (pueden probarse) redundantes y podrían eliminarse.

Ahora eso es solo teoría, o matemáticas. Para que el optimizador realice una eliminación real, la teoría debe haberse convertido en código y agregado en el conjunto de optimizaciones / reescrituras / eliminaciones del optimizador. Para que eso suceda, los desarrolladores (DBMS) deben pensar que tendrá buenos beneficios para la eficiencia y que es un caso bastante común.

Personalmente, no suena como uno (lo suficientemente común). La consulta, como admite, parece bastante tonta y un revisor no debería dejar que pase la revisión, a menos que se haya mejorado y se elimine la unión redundante.

Dicho esto, hay consultas similares donde ocurre la eliminación. Hay una muy buena publicación de blog relacionada por Rob Farley: JOIN simplification in SQL Server .

En nuestro caso, todo lo que tenemos que hacer es cambiar las uniones por LEFTuniones. Ver dbfiddle.uk . El optimizador en este caso sabe que la unión se puede eliminar de forma segura sin posiblemente cambiar los resultados. (La lógica de simplificación es bastante general y no está especialmente diseñada para autouniones).

Por supuesto, en la consulta original, eliminar las INNERuniones tampoco puede cambiar los resultados. Pero no es común unirse por sí mismo en la clave principal, por lo que el optimizador no tiene este caso implementado. Sin embargo, es común unirse (o combinación izquierda) donde la columna unida es la clave principal de una de las tablas (y a menudo hay una restricción de clave externa). Lo que lleva a una segunda opción para eliminar las uniones: Agregue una restricción de clave externa (¡autoreferenciada!):

ALTER TABLE "Table"
    ADD FOREIGN KEY (id) REFERENCES "Table" (id) ;

Y voila, se eliminan las uniones! (probado en el mismo violín): aquí

create table docs
(id int identity primary key,
 doc varchar(64)
) ;
GO
insert
into docs (doc)
values ('Enter one batch per field, don''t use ''GO''')
     , ('Fields grow as you type')
     , ('Use the [+] buttons to add more')
     , ('See examples below for advanced usage')
  ;
GO
4 filas afectadas
--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    join docs d2 on d2.id=d1.id
    join docs d3 on d3.id=d1.id
    join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | Doc                                      
-: | : ----------------------------------------
 1 | Ingrese un lote por campo, no use 'IR'
 2 | Los campos crecen a medida que escribe                  
 3 | Use los botones [+] para agregar más          
 4 | Vea los ejemplos a continuación para uso avanzado    

ingrese la descripción de la imagen aquí

--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    left join docs d2 on d2.id=d1.id
    left join docs d3 on d3.id=d1.id
    left join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | Doc                                      
-: | : ----------------------------------------
 1 | Ingrese un lote por campo, no use 'IR'
 2 | Los campos crecen a medida que escribe                  
 3 | Use los botones [+] para agregar más          
 4 | Vea los ejemplos a continuación para uso avanzado    

ingrese la descripción de la imagen aquí

alter table docs
  add foreign key (id) references docs (id) ;
GO
--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    join docs d2 on d2.id=d1.id
    join docs d3 on d3.id=d1.id
    join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | Doc                                      
-: | : ----------------------------------------
 1 | Ingrese un lote por campo, no use 'IR'
 2 | Los campos crecen a medida que escribe                  
 3 | Use los botones [+] para agregar más          
 4 | Vea los ejemplos a continuación para uso avanzado    

ingrese la descripción de la imagen aquí

ypercubeᵀᴹ
fuente
2

En términos relacionales, cualquier autounión sin cambio de nombre de atributo es un no-op y se puede eliminar de forma segura de los planes de ejecución. Desafortunadamente, SQL no es relacional y la situación en la que el optimizador puede eliminar una autounión se limita a un pequeño número de casos extremos.

La sintaxis SELECT de SQL da prioridad lógica de combinación sobre la proyección. Las reglas de alcance de SQL para los nombres de columna y el hecho de que se permiten nombres de columna duplicados y columnas sin nombre hace que la optimización de consultas SQL sea significativamente más difícil que la optimización del álgebra relacional. Los proveedores de SQL DBMS tienen recursos finitos y deben ser selectivos sobre qué tipos de optimización desean admitir.

nvogel
fuente
1

Las claves primarias siempre son únicas y los valores nulos no están permitidos, por lo que unir una tabla a sí mismo en las claves primarias (no con una clave secundaria autorreferenciada y sin sentencias where) producirá el mismo número de filas que la tabla original.

En cuanto a por qué no lo optimizan, diría que es un caso marginal que no planearon o asumieron que las personas no harían. Unirse a una tabla en sí mismo en claves primarias únicas garantizadas no sirve para nada.

indiri
fuente