2009-04-17 15 views
20

Después de preparar una respuesta para this question Encontré que no pude verificar mi respuesta.¿Cómo funciona el predicado IN en SQL?

En mi primer trabajo de programación me dijeron que una consulta dentro del predicado IN() se ejecuta para cada fila contenida en la consulta principal, y por lo tanto, debe evitarse utilizar IN.

Por ejemplo, dada la consulta:

SELECT count(*) FROM Table1 WHERE Table1Id NOT IN (
SELECT Table1Id FROM Table2 WHERE id_user = 1) 
 
Table1 Rows | # of "IN" executions 
---------------------------------- 
     10 |  10 
    100 |  100 
    1000 |  1000 
    10000 | 10000 

Es esto correcto? ¿Cómo funciona realmente el predicado IN?

+4

Muy buena pregunta. No veo muchas preguntas que piden un desglose de cómo ciertas cosas en SQL realmente funcionan. Muchas veces las personas se preocupan más por obtener los resultados que desean. – TheTXI

Respuesta

16

la advertencia que tienes acerca de subconsultas ejecución para cada fila es cierto - para subconsultas correlacionadas .

SELECT COUNT(*) FROM Table1 a 
WHERE a.Table1id NOT IN (
    SELECT b.Table1Id FROM Table2 b WHERE b.id_user = a.id_user 
); 

Tenga en cuenta que los subconsulta hace referencia a la columna de la id_user de la consulta externa. El valor de id_user en cada fila de Table1 puede ser diferente. Entonces, el resultado de la subconsulta probablemente será diferente, dependiendo de la fila actual en la consulta externa. El RDBMS debe ejecutar la subconsulta muchas veces, una para cada fila en la consulta externa.

El ejemplo que ha probado es una subconsulta no correlacionada. La mayoría de los optimizadores de RDBMS modernos que valen la pena deberían ser capaces de determinar cuándo el resultado de la subconsulta no depende de los valores de cada fila de la consulta externa. En ese caso, el RDBMS ejecuta la subconsulta una sola vez, guarda su resultado en caché y lo usa repetidamente para el predicado en la consulta externa.

PD: En SQL, IN() se denomina "predicado", no es una afirmación. Un predicado es una parte del lenguaje que se evalúa como verdadero o falso, pero no necesariamente se puede ejecutar de forma independiente como una declaración. Es decir, no puede ejecutar esto como una consulta SQL: "2 IN (1,2,3);" Aunque este es un predicado válido, no es una declaración válida.

+0

+1 Gran respuesta; ¡También gracias por la lección de terminología! –

+0

En MySQL, la subconsulta se optimizará para NOT EXISTS (que es SUBQUERY DEPENDIENTE), y usará acceso de índice en 'INDEX ON Table2 (id_user, table1ID)', si existe. – Quassnoi

0

No realmente. Pero es mantequilla escribir tales consultas usando JOIN

4

Depende del optimizador. Compruebe exactamente query plan para cada consulta en particular para ver cómo el RDBMS realmente ejecutará eso.

En Oracle eso sería:

EXPLAIN PLAN FOR «your query» 

En MySQL o PostgreSQL

EXPLAIN «your query» 
+1

@vartec - ¿algún recurso sobre la lectura de planes de consulta? Este es un lado ciego de mi conocimiento y todavía tengo que encontrar un artículo decente en línea. –

+0

es dependiente de DBMS, pero básicamente eso le da idea de qué operación básica va a hacer y en qué orden, qué índice usará, etc. – vartec

+0

Creo que lo que tendría que buscar sería un artículo sobre la sintonización de DB, como este es un propósito de mirar los planes de consulta en primer lugar. – vartec

8

Es del todo dependerá de la base de datos que está utilizando, y la consulta exacta.

Los optimizadores de consultas son muy inteligentes a veces: en su consulta de muestra, espero que las mejores bases de datos puedan usar el mismo tipo de técnicas que utilizan con una combinación. Las bases de datos más ingenuas pueden ejecutar la misma consulta muchas veces.

+1

Estoy de acuerdo, acabo de hacer una consulta de prueba y creo (no soy bueno en la lectura de ExecutionPlans) se creó una unión interna. –

3

La mayoría de los motores SQL hoy en día casi siempre van a crear el mismo plan de ejecución de LEFT JOIN, NO EN y NO EXISTE

diría vistazo a su plan de ejecución y averiguar :-)

También si tiene valores NULL para la columna Table1Id no obtendrá ningún dato devuelto

5

Esto depende del RDBMS en cuestión.

Ver análisis detallado aquí:

En s hort:

  1. MySQL optimizará la consulta a este:

    SELECT COUNT(*) 
    FROM Table1 t1 
    WHERE NOT EXISTS 
         (
         SELECT 1 
         FROM Table2 t2 
         WHERE t2.id_user = 1 
           AND t2.Table1ID = t1.Table2ID 
         ) 
    

    y ejecutar la subconsulta interior en un bucle, utilizando el índice de búsqueda de cada vez.

    • SQL Server usarán MERGE ANTI JOIN.

    La subconsulta interna no se "ejecutará" en un sentido común de palabra, en su lugar, los resultados de consulta y subconsulta se obtendrán al mismo tiempo.

    Consulte el enlace de arriba para una explicación más detallada.

    • Oracle usará HASH ANTI JOIN.

    La subconsulta interna se ejecutará una vez, y se generará una tabla hash a partir del conjunto de resultados.

    Los valores de la consulta externa se buscarán en la tabla hash.

    • PostgreSQL usarán NOT (HASHED SUBPLAN).

    Mucho me gusta Oracle.

Tenga en cuenta que volver a escribir la consulta como esta:

SELECT (
     SELECT COUNT(*) 
     FROM Table1 
     ) - 
     (
     SELECT COUNT(*) 
     FROM Table2 t2 
     WHERE (t2.id_user, t2.Table1ID) IN 
       (
       SELECT 1, Table1ID 
       FROM Table1 
       ) 
     ) 

se espera mejorar considerablemente el rendimiento en los cuatro sistemas.

+0

La última vez que busqué, SQL Server solo permite que se devuelva una sola columna de una subconsulta que luego se usa en un predicado IN. ¿Ha cambiado eso? – RolandTumble

+0

@RolandTumble: no. 'MySQL' y' PostgreSQL' permiten eso, 'SQL Server' y' Oracle' no. Para los dos últimos sistemas, el predicado 'IN' debe reescribirse como' EXISTS'. – Quassnoi

0

Sí, pero la ejecución se detiene tan pronto como el procesador de consultas "encuentra" el valor que está buscando ... Así que si, por ejemplo, la primera fila en el exterior selecciona Table1Id = 32, y si Table2 tiene un registro con un IDtabla = 32, entonces tan pronto como la subconsulta encuentra la fila en la Tabla 2, donde IDtabla = 32, se detiene ...

Cuestiones relacionadas