¿Qué es más eficiente, una cláusula where o una combinación con más de un millón de tablas de filas?

17

Ejecutamos un sitio web que tiene filas de 250MM en una tabla y en otra tabla a la que nos unimos para la mayoría de las consultas tiene menos de 15MM de filas.

Estructuras de muestra:

MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows

Regularmente tenemos que hacer algunas consultas en todas estas tablas. Una es obtener estadísticas para usuarios gratuitos (~ 10k usuarios gratuitos).

Select Count(1) from DetailsTable dt 
join MasterTable mt on mt.Id = dt.MasterId 
join UserTable ut on ut.Id = mt.UserId 
where ut.Role is null and mt.created between @date1 and @date2

El problema es que esta consulta algunas veces durará mucho tiempo debido al hecho de que las uniones ocurren mucho antes del where.

En este caso, ¿sería más sabio usar dónde en lugar de unirse o posiblemente where column in(...)?

Jeremy Boyd
fuente
1
¿Qué base de datos y versión?
Leigh Riffel
1
¿Has probado en ambos sentidos?
gbn
Si se tratara de Oracle, crearía un índice basado en funciones para UserTable en NVL2 (Role, NULL, ID), pero este parece otro DB.
Leigh Riffel

Respuestas:

20

Para los RDBMS modernos no hay diferencia entre "JOIN explícito" y "JOIN-in-the-WHERE" (si todas las UNIONES son INTERIORES) en lo que respecta al rendimiento y al plan de consulta.

La sintaxis explícita JOIN es más clara y menos ambigua (ver enlaces a continuación)

Ahora, JOIN-before-WHERE es un procesamiento lógico , no un procesamiento real , y los optimizadores modernos son lo suficientemente inteligentes como para darse cuenta de esto.

Su problema aquí es probablemente la indexación.

Muéstrenos todos los índices y claves en estas tablas. Y los planes de consulta

Nota: esta pregunta habría estado cerca en StackOverflow por ser un duplicado en este momento ... COUNT (1) vs COUNT (*) es otro mito reventado también.

gbn
fuente
2
NO SIEMPRE ES VERDAD que no hay diferencia entre joiny wherecláusula. Optimizo las consultas de larga duración todo el tiempo y, a veces, las consultas que usan la wherecláusula funcionan mejor que las que usan joinen un factor de hasta 70x. Si fuera así de simple y directo, la vida sería todo arcoíris y unicornios. Y esto no se trata de un antiguo motor oscuro: en este momento estoy mirando la ventaja 70x de la wherecláusula en SQL 2012.
ajeh
Aún más, a menudo observo los mismos planes exactos de ambos enfoques y las consultas aisladas realizan exactamente lo mismo, pero cuando la whereconsulta de la cláusula se ejecuta dentro del lote grande del que se supone que forma parte, supera a la joinconsulta por un margen enorme. Las consultas SQL no se ejecutan al vacío: se ven afectadas por el resto de la carga útil del servidor y, a menudo, las whereconsultas de la cláusula funcionan bastante bien, lo cual es una molestia ya que la joinsintaxis es mucho más limpia.
ajeh
3
@ajeh: Sugeriría que su experiencia es muy atípica. Tiene problemas más grandes con las consultas si tiene diferencias x70: es así de simple
gbn
5

Tienes que refactorizar la consulta por completo

Intente ejecutar las cláusulas WHERE antes y las UNIONES más tarde

Select Count(1) from DetailsTable dt
join (Select UserId,Id FROM MasterTable where
created between @date1 and @date2) mt on mt.Id = dt.MasterId 
join (Select Id FROM UserTable WHERE Role is NULL) ut
on ut.Id = mt.UserId;

Incluso si ejecuta un plan EXPLAIN en esta consulta refactorizada y se ve peor que su original, intente de todos modos. Las tablas temporales creadas internamente realizarán uniones cartesianas, pero esas tablas son más pequeñas para trabajar.

Tengo esta idea de este video de YouTube .

Probé los principios del video en una pregunta muy compleja en StackOverflow y obtuve una recompensa de 200 puntos.

@gbn mencionó asegurarse de tener los índices correctos en su lugar. En este caso, indexe la columna creada en MasterTable.

Darle una oportunidad !!!

ACTUALIZACIÓN 2011-06-24 22:31 EDT

Debe ejecutar estas consultas:

SELECT COUNT(1) AllRoles FROM UserTable;
SELECT COUNT(1) NullRoles FROM UserTable WHERE Role is NULL;

Si NullRoles X 20 <AllRoles (en otras palabras, si NullRoles es inferior al 5% de las filas de la tabla), debe crear un índice no único de la función en UserTable. De lo contrario, una tabla completa de UserTable sería suficiente, ya que el Optimizador de consultas posiblemente descarte el uso de un índice.

ACTUALIZACIÓN 2011-06-25 12:40 EDT

Como soy un DBA MySQL, mi método de hacer las cosas requiere no confiar en el Optimizador de consultas MySQL a través del pesimismo positivo y ser conservador. Por lo tanto, intentaré refactorizar una consulta o crear los índices de cobertura necesarios para adelantarnos a los malos hábitos ocultos del Optimizador de consultas MySQL. La respuesta de @ gbn parece más completa, ya que SQL Server puede tener más "solidez mental" al evaluar las consultas.

RolandoMySQLDBA
fuente
0

Teníamos una tabla [Detalle] de aproximadamente 75 millones de filas; una tabla [Master] de aproximadamente 400K filas y una tabla [Item] relacionada que tenía 7 filas, siempre y para siempre. Almacenaba el pequeño conjunto de "números de artículos" (1-7) y modelaba un formulario en papel, millones de los cuales se imprimían y distribuían cada mes. La consulta más rápida fue la que probablemente menos pensaría primero, que implica el uso de una unión cartesiana. IIRC, fue algo así como:

SELECT m.order_id, i.line_nr, d.Item_amt
FROM Master m, Item i 
INNER JOIN Detail d ON m.order_id = d.order_id

Aunque existe un enlace lógico de "id" entre [Item] y [Detail], CROSS JOIN funcionó mejor que INNER JOIN.

El RDBMS era Teradata con su tecnología MPP, e IDR lo que era el esquema de indexación. La tabla de 7 filas no tenía índice, ya que TABLE SCAN siempre funcionaba mejor.

Timothy Oleary
fuente