2012-10-03 25 views
5

Entiendo que las subconsultas son notoriamente malas para el rendimiento cuando se usan incorrectamente. Tengo un escenario muy específico donde el usuario necesita recuperar un conjunto filtrado de registros de una tabla. Una amplia variedad de filtros estará disponible y deben ser compatibles con la composición. Además, un grupo de desarrolladores creará nuevos filtros de forma regular.Subconsultas SQL en la cláusula FROM

No me gusta la idea de una consulta SQL monolítica y creciente con una cantidad bruta de parámetros. No me gusta la idea de un grupo de consultas SQL autónomas con sentencias SELECT idénticas y cláusulas WHERE variables. ME GUSTA la idea de una consulta SQL dinámica, pero no estoy seguro de qué tipo de estructura debería usar. No puedo pensar en 4 opciones básicas: (si hay más que me falta, entonces por favor no dude en sugerir ellas)

  1. "INNER JOIN": concatenar los filtros a través de uniones internas para filtrar los resultados.
  2. "FROM subqueries": Filtros de pila a través de subconsultas en la instrucción FROM.
  3. "WHERE subconsultas": filtros contactados a través de subconsultas en la cláusula WHERE.
  4. "subconsultas INNER JOIN": un híbrido extraño.

He creado un violín SQL para demostrar (y perfil) de ellos:

http://sqlfiddle.com/#!3/4e17b/9

El siguiente es un extracto del violín para dar una idea de lo que yo' estoy hablando:

------------------------------------------------------------------------ 
--THIS IS AN EXCERPT FROM THE SQL FIDDLE -- IT IS NOT MEANT TO COMPILE-- 
------------------------------------------------------------------------ 

-- 
--"INNER JOIN" test 
     SELECT COUNT(*) 
     FROM 
      @TestTable Test0 
      INNER JOIN @TestTable Test1 ON Test1.ID=Test0.ID AND Test1.ID % @i = 0 
      INNER JOIN @TestTable Test2 ON Test2.ID=Test0.ID AND Test2.ID % @j = 0 
      INNER JOIN @TestTable Test3 ON Test3.ID=Test0.ID AND Test3.ID % @k = 0 

-- 
--"FROM subqueries" test 
     SELECT COUNT(*) FROM (
      SELECT * FROM (
        SELECT * FROM (
         SELECT * FROM @TestTable Test3 WHERE Test3.ID % @k = 0 
       ) Test2 WHERE Test2.ID % @j = 0 
      ) Test1 WHERE Test1.ID % @i = 0 
    ) Test0 

-- 
--"WHERE subqueries" test 
     SELECT COUNT(*) 
     FROM @TestTable Test0 
     WHERE 
      Test0.ID IN (SELECT ID FROM @TestTable Test1 WHERE Test1.ID % @i = 0) 
      AND Test0.ID IN (SELECT ID FROM @TestTable Test2 WHERE Test2.ID % @j = 0) 
      AND Test0.ID IN (SELECT ID FROM @TestTable Test3 WHERE Test3.ID % @k = 0) 

-- 
--"INNER JOIN subqueries" test 
    SELECT COUNT(*) 
    FROM 
     TestTable Test0 
     INNER JOIN (SELECT ID FROM TestTable WHERE ID % @i = 0) Test1 ON Test1.ID=Test0.ID 
     INNER JOIN (SELECT ID FROM TestTable WHERE ID % @j = 0) Test2 ON Test2.ID=Test0.ID 
     INNER JOIN (SELECT ID FROM TestTable WHERE ID % @k = 0) Test3 ON Test3.ID=Test0.ID 

-- 
--"EXISTS subqueries" test 
    SELECT COUNT(*) 
    FROM TestTable Test0 
    WHERE 
     EXISTS (SELECT 1 FROM TestTable Test1 WHERE Test1.ID = Test0.ID AND Test1.ID % @i = 0) 
     AND EXISTS (SELECT 1 FROM TestTable Test2 WHERE Test2.ID = Test0.ID AND Test2.ID % @j = 0) 
     AND EXISTS (SELECT 1 FROM TestTable Test3 WHERE Test3.ID = Test0.ID AND Test3.ID % @k = 0) 

Clasificaciones (tiempo para ejecutar las pruebas)

SQL violín:

|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES| 
------------------------------------------------------------------------------------- 
|  5174 |   777 |   7240 |    5478 |   7359 | 

Medio Ambiente Local: (sin caché: Borrado de memoria intermedia antes de cada prueba)

|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES| 
------------------------------------------------------------------------------------- 
|  3281 |   2851 |   2964 |    3148 |   3071 | 

Medio Ambiente Local: (con caché: ejecutar consultas dos veces seguidas y registrar la hora de la segunda ejecución)

|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES| 
------------------------------------------------------------------------------------- 
|  284 |   50 |   3334 |     278 |    408 | 

Existen ventajas/desventajas con cada solución. Las subconsultas en la cláusula WHERE tienen un rendimiento bastante terrible. Las subconsultas en la cláusula FROM tienen un rendimiento bastante bueno (en realidad, generalmente funcionan mejor) (NOTA: ¿Creo que este método anularía los beneficios de los índices?). Las INNER JOINs tienen un rendimiento bastante bueno, aunque introducen algunos aspectos de alcance interesantes porque a diferencia de las subconsultas, INNER JOINs funcionaría en el mismo contexto (tendría que haber un sistema intermediario para evitar la colisión de alias de tabla).

En general, creo que la solución más limpia es subconsultas en la cláusula FROM. Los filtros serían fáciles de escribir y probar (porque a diferencia de INNER JOINs no necesitarían ser provistos con una consulta de contexto/base).

¿Pensamientos? ¿Es este un uso válido de subconsultas o un desastre esperando a suceder?

ACTUALIZACIÓN (10/04/2012):

  • Actualizado SQL violín a incluir una prueba de "existe" método
  • prueba de rendimiento de SQL Agregado de violín y medio ambiente local

Respuesta

0

Si siempre vas a aplicar "y" la lógica de la combinación interna es probablemente un buen enfoque (estoy generalizando, pero va a variar por una gran cantidad de factores, incluyendo el tamaño de la tabla y los índices, etc.). Tendrá que utilizar una de las otras soluciones si desea poder aplicar el filtrado "y" o "o".

Además, debe probar el rendimiento utilizando existe cláusulas:

SELECT COUNT(*) 
     FROM @TestTable Test0 
     WHERE 
      EXISTS (SELECT 1 FROM @TestTable Test1 WHERE Test0.ID = Test1.ID AND Test1.ID % @i = 0) 
      EXISTS (SELECT 1 FROM @TestTable Test2 WHERE Test0.ID = Test2.ID AND Test2.ID % @j = 0) 
      EXISTS (SELECT 1 FROM @TestTable Test3 WHERE Test0.ID = Test3.ID AND Test3.ID % @k = 0) 
+0

pruebas agregadas para el método "existe". Se ubica cerca del extremo final del paquete, aunque me sorprendió ver alguna diferencia entre "SELECT *" y "SELECT 1" (ciertamente no eran las pruebas más controladas, pero había una variación definida). La metodología general se aplicará a una variedad de tablas, por lo que el tamaño y los índices son difíciles de predecir con antelación. Básicamente estoy buscando la "caja de arena más amigable". – makerplays

Cuestiones relacionadas