Me doy cuenta de que cuando hay eventos de derrame a tempdb (que causan consultas lentas), a menudo las estimaciones de fila están muy lejos para una unión en particular. He visto que se producen eventos de derrame con combinaciones de combinación y hash y, a menudo, aumentan el tiempo de ejecución de 3x a 10x. Esta pregunta se refiere a cómo mejorar las estimaciones de filas bajo el supuesto de que reducirá las posibilidades de eventos de derrames.
Número real de filas 40k.
Para esta consulta, el plan muestra una estimación de filas incorrectas (11.3 filas):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Para esta consulta, el plan muestra una buena estimación de filas (56k filas):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
¿Se pueden agregar estadísticas o sugerencias para mejorar las estimaciones de filas para el primer caso? Intenté agregar estadísticas con valores de filtro particulares (propiedad = 2840) pero no pude obtener la combinación correcta o tal vez se está ignorando porque el ObjectId es desconocido en el momento de la compilación y podría estar eligiendo un promedio sobre todos los ObjectIds.
¿Hay algún modo en el que primero haga la consulta de la sonda y luego la use para determinar las estimaciones de fila o debe volar a ciegas?
Esta propiedad particular tiene muchos valores (40k) en algunos objetos y cero en la gran mayoría. Estaría contento con una pista donde se podría especificar el número máximo esperado de filas para una unión determinada. Este es un problema generalmente inquietante porque algunos parámetros pueden determinarse dinámicamente como parte de la unión o estarían mejor ubicados dentro de una vista (no se admiten variables).
¿Hay algún parámetro que pueda ajustarse para minimizar la posibilidad de derrames en tempdb (por ejemplo, memoria mínima por consulta)? El plan robusto no tuvo efecto en la estimación.
Editar 2013.11.06 : Respuesta a comentarios e información adicional:
Aquí están las imágenes del plan de consulta. Las advertencias son sobre el predicado cardinalidad / búsqueda con el convert ():
Según el comentario de @Aaron Bertrand, intenté reemplazar el convert () como prueba:
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
Como un punto de interés extraño pero exitoso, también le permitió cortocircuitar la búsqueda:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Ambos enumeran una búsqueda de clave adecuada, pero solo los primeros enumeran una "Salida" de ObjectId. ¿Supongo que eso indica que el segundo es realmente un cortocircuito?
¿Alguien puede verificar si alguna vez se realizan sondas de una sola fila para ayudar con las estimaciones de fila? Parece incorrecto limitar la optimización solo a estimaciones de histograma cuando una búsqueda PK de una sola fila puede mejorar en gran medida la precisión de la búsqueda en el histograma (especialmente si hay potencial o historial de derrames). Cuando hay 10 de estas subuniones en una consulta real, idealmente estarían sucediendo en paralelo.
Una nota al margen, dado que sql_variant almacena su tipo base (SQL_VARIANT_PROPERTY = BaseType) dentro del campo en sí, esperaría que un convert () sea casi sin costo siempre que sea convertible "directamente" (por ejemplo, no cadena a decimal, sino int a int o tal vez int para bigint). Como eso no se conoce en el momento de la compilación, pero puede ser conocido por el usuario, tal vez una función "AssumeType (type, ...)" para sql_variants les permita ser tratados de manera más transparente.
fuente
declare @a bigint =
como lo ha hecho me parece una solución natural, ¿por qué es inaceptable?CONVERT()
en columnas y luego unirlas. Esto ciertamente no es eficiente en la mayoría de los casos. En este caso en particular, solo se debe convertir un valor, por lo que probablemente no sea un problema, pero ¿qué índices tiene en la tabla? Los diseños de EAV generalmente funcionan bien, solo con una indexación adecuada (lo que significa una gran cantidad de índices en las tablas generalmente estrechas).Respuestas:
No comentaré sobre derrames, tempdb o sugerencias porque la consulta parece bastante simple para necesitar tanta consideración. Creo que el optimizador de SQL-Server hará su trabajo bastante bien, si hay índices adecuados para la consulta.
Y su división en dos consultas es buena, ya que muestra qué índices serán útiles. La primera parte:
necesita un índice para
(PropertyId, ObjectId, Sequence)
incluir elValue
. Lo haríaUNIQUE
para estar a salvo. La consulta arrojaría un error de todos modos durante el tiempo de ejecución si se devuelven más de una fila, por lo que es bueno asegurarse de antemano que esto no sucederá, con el índice único:La segunda parte de la consulta:
necesita un índice que
(PropertyId, ObjectId)
incluyaValue
:Si no se mejora la eficiencia o si estos índices no se utilizaron o aún existen diferencias en las estimaciones de filas, entonces sería necesario investigar más a fondo en esta consulta.
En ese caso, las conversiones (necesarias del diseño EAV y el almacenamiento de diferentes tipos de datos en las mismas columnas) son una causa probable y su solución de división (como comentan @AAron Bertrand y @Paul White) la consulta en dos partes parece natural y el camino a seguir. Un rediseño para tener diferentes tipos de datos en sus respectivas columnas podría ser otro.
fuente
Como respuesta parcial a la pregunta explícita sobre cómo mejorar las estadísticas ...
Tenga en cuenta que las estimaciones de filas incluso para el caso separado por separado todavía están desactivadas en 10X (4k frente a 40k esperado).
El histograma de estadísticas probablemente se extendió demasiado para esa propiedad porque es una tabla larga (vertical) de 3.5M filas y esa propiedad particular es extremadamente escasa.
Cree estadísticas adicionales (algo redundantes con las estadísticas IX) para la propiedad dispersa:
Los originales:
Con convert () eliminado (adecuado):
Con convert () eliminado (short-ciruit):
Aún apagado por ~ 2X probablemente porque> 99.9% de los objetos no tienen la Propiedad 2840 definida en absoluto. De hecho, solo para este caso de prueba, la propiedad existe solo en 1 de 200k Objetos distintos de la tabla de filas de 3.5M. Es sorprendente que se haya acercado tanto. Ajustando el filtro para que tenga menos ObjectIds,
Hmm, no hay cambios ... Respaldo que agregó "con exploración completa" al final de las estadísticas (podría ser la razón por la que los dos anteriores no funcionaron) y sí:
Hurra. Por lo tanto, en una tabla altamente vertical con un IX que cubre ampliamente, agregar estadísticas filtradas adicionales parece ser una gran mejora (especialmente para combinaciones de teclas dispersas pero muy variantes).
fuente