Usando subconsultas en SQL, para reducir la complejidad de nuestros queries

Siempre que tengamos consultas complejas, donde intervienen varias tablas y además hay varios filtros, se nos puede hacer un lío. Ya hay muy buenos artículos sobre el uso subconsultas con SQL en la web: The Power of Subqueries, Using a Subquery in a T-SQL Statement.

Sólo voy a dar un ejemplo sencillo de como usar estas. Tenemos el siguiente modelo, por cierto es un modelo básico, a noticia verdadera le faltan muchos más campos:

Y se me presenta la necesidad de traer todas las noticias con el tag ‘Microsoft’. La consulta sería asi:

   SELECT * FROM Noticias.Noticia noti
        WHERE noti.NOTCod in (
                   SELECT notTag.NOTCod 
                   FROM Noticias.Tag tag
                        INNER JOIN Noticias.NoticiaTag notTag 
                        ON tag.TAGCod = notTag.TAGCod
                   WHERE tag.TAGNombre = ‘Microsoft’ )

Como pueden apreciar, en este caso la subconsulta devuelve todos los código de las noticias con el tag enviado, y sólo recuperaré las noticias cuyo código esté dentro de ese rango. Podrían decir que con sólo usar JOINs se puede también hacer, pero normalmente nuestras tablas no tendrán esta estructura, la cantidad de tablas que participan en la consulta también será mayor, y habrá muchos más filtros, por ejemplo filtro por el área, filtro por las noticias publicadas, entre otros. En el caso de que quisiéramos filtrar las noticias publicadas, sólo habría que agregar un AND y la condición al WHERE de la consulta de arriba.

Las subconsultas se pueden usar en:

  • En el listado de las columnas de una consulta SELECT.
  • En una clausula WHERE, el ejemplo de arriba.
  • En una clausula FROM.
  • En una clausula FROM de una consulta UPDATE.
  • En una clausula HAVING.

Para revisar ejemplos de uso de subconsultas, visitar:

Por cierto, revisaba un post de Percy Reyes, donde hablaba de SubQueries vs INNER JOINs, y en uno de los comentarios, PabloNetrix, comentaba que tenía consultas que toman 45 minutos. Y es que si queremos optimizar queries, no sólo debemos valernos de subconsultas, ya Rodrigo, hablo del uso de índices, también se debe hacer uso del plan de ejecución de SQL, para observar, interpretar, y tunear nuestros queries. Además, para saber si lo que estamos haciendo va bien, o que más podemos hacer. Por cierto encontré estos links: Cómo solucionar problema de consultas de ejecución lenta en SQL Server 7.0 o versiones posteriores, y Mostrar planes de ejecución gráficos (SQL Server Management Studio).

Saludos,

Post cruzado 3Dev Blogs

22 comentarios en “Usando subconsultas en SQL, para reducir la complejidad de nuestros queries”

  1. Creo que “exists” para estos casos es mas eficiente que la “in”:

    SELECT * FROM Noticias.Noticia noti

    WHERE exists (SELECT 1
    FROM Noticias.Tag tag INNER JOIN Noticias.NoticiaTag notTag
    ON tag.TAGCod = notTag.TAGCod
    WHERE tag.TAGNombre = ‘Microsoft’ and notTag.NotCod = noti.NOTCod)

  2. Hola Pirri!

    Muchas gracias por el aporte, no sabía que se podía hacer de esa forma, aún estoy masticando la consulta :p.

    Lo estuve probando en diversas tablas con más cantidad de datos, el plan de ejecución siempre es el mismo para los dos. Habría que probarlo con “más” datos, con consultas que tomen más de medio minuto para ver si hay diferencias en el tiempo que tarda cada consulta.

    Pregunta: para los tigres en optimización de queries, si dos consultas tienen el mismo plan de ejecución, tienen el mismo grado de eficiencia?, o no necesariamente?

    Saludos,

  3. “…suele ser mas eficiente que In porque mientras que este necesita que se obtengan todos los registros de la subquery, con el exists la subquery finaliza en cuanto encuentre el primer registro que cumpla la condicion.”

    Lo cual efectivamente tiene su lógica.

    Por cierto una duda: Donde has escrito
    “WHERE exists (SELECT 1”,
    ¿no habrás querido decir realmente
    “WHERE exists (SELECT TOP 1” ?

    Una curiosidad más acerca de las consultas (bueno en este caso, en realidad es acerca del Optimizador): ¿Sabíais que hacer

    “SELECT COUNT(*) FROM…”

    es lo mismo que

    “SELECT COUNT(CampoID) FROM…” ?

    Pues sí, comprobado con el Analizador. 🙂

    Saludos

  4. No, donde digo select 1 quiero decir select 1. La clausula exists no necesita devolver datos, asi que en vez de poner un * o el nombre de un campo pues pongo el dato mas pequeño que se me ocurre: un 1. Con eso es bastante. Pero podia haber puesto “select noti.NOTCod” o “select ‘hola'”…

    Con respecto al select count, tambien funciona si pones select count(1) 😛

    Saludos!

  5. Que loko!. Ahora si queda claro, bueno eso creo, gracias por el dato Pirri!

    Resumiendo el detalle para todos: Comparando las clauslas EXISTS e IN en los escenarios mostrarados arriba. Las dos consultas retornan los mismos datos, usando IN, por cada fila recupera el subquery (con todos los datos) y compara si esta en ese rango. En cambio con EXISTS, por cada fila veríciará si existe esa fila del subquery, el SELECT 1, en este caso trabajará como un booleano, ya que si existe el registro retornará una fila con una celda 1, y si no retorna cero filas, el exists verificará si existen filas, osea que es igual poner SELECT 1, SELECT 2, o el numero que se me ocurrá, colocar un string, o poner *, es distinto, ya el resultado será el mismo, sólo se usa como un flag.

    Pirri, hay algo más que se necesite detallar?

    Saludos,

  6. Hola me gustó mucho tu artículo. Solo tengo una pregunta. Estoy realizando subconsultas en un select, para traer una columna extra con el promedio de los salarios de los clientes. Me dicen que eso es ineficiente. Que es mejor realizar un join con una subconsulta que traiga dicho total. Al realizar los test de performance de ambas, es mas eficiente la primera. Preciento que es debido a que realizar el join con una subconsulta, es crear una tabla “temporal” que no posee indices. Si esta bien mi apreciación?. Cual sería la mejor manera de evaluarlo?.

  7. Tengo dos Tablas A y B relacionadas de 1 a N , A es la Entidad Fuerte y B la debil.

    Entonces tenemos que cada Registro en la Tabla A tiene N Registros en la Tabla B.

    Bien lo que necesto es sacar Los Ultimos registros de la Tabla B , para cada registro en A .

    La verdad lo he pensado de muchas formas y no he podido , desde ya les agradezco.

  8. a que exactamente te refieres con sacar los últimos registros?, puedes usar la clausula WHERE (filtros) y TOP (para sacar los primeros n registros). Si quisieras los ultimos registros de la consulta, puedes hacer un ORDER BY inverso, y usar un TOP.

    Saludos,

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *