Intersect, Except, Union, All and Any - David Poole

Posted on
  • martes, 21 de julio de 2009
  • by
  • Román
  • in
  • Etiquetas: , , , , ,
  • David Poole en SQLServerCentral hace una revisión de algunos nuevos comandos en SQL Server 2008, por caso:

    INTERSECT
    EXCEPT
    ALL
    ANY
    ALL y ANY no son nuevos, pero INTERSECT y EXCEPT son nuevos.

    INTERSECT, EXCEPT and UNION

    Para experimentar con esos comandos David decidió ver dos conjuntos de valores para CustomerID.

    Customers (Clientes) en sales territory 10 (United Kingdom)
    (pedidos) Sales orders de July 2004, el cual es el último mes de pedidos en Adventureworks
    La mejor manera de ver que hacen estos dos comandos es comparar los datos en los diagramas.
    redicate Illustration Description
    EXCEPT Customers (clientes) de UK que no compraron en Julio 2004
    INTERSECT Customers (clientes) de UK
    AND (y) que compraron algo en Julio 2004
    UNION Customers (clientes) de UK
    OR (o) que hicieron una compra en Julio 2004

    Diferentes formas de escribir un query con EXCEPT.

    Si bien nunca usé EXCEPT antes yo he obtenido los mismos resultados por métodos tradicionales, hay muchas formas de hacer lo mismo, por ejemplo:
    LEFT JOIN
    Este query trae los resultados requeridos.
    SELECT C.CustomerID
       FROM Sales.Customer AS C
        LEFT JOIN Sales.SalesOrderHeader AS OH
        ON C.CustomerID = OH.CustomerID
        AND OrderDate>='2004-07-01'
       WHERE OH.CustomerID IS NULL
       AND C.TerritoryID=10
    WHERE CustomerID NOT IN(…)
    Buscando la eficiencia, otra forma de hacer lo mismo.
    
    SELECT CustomerID
       FROM Sales.Customer
       WHERE TerritoryID=10
       AND CustomerID NOT IN(
        SELECT customerid
        FROM  Sales.SalesOrderHeader
        WHERE OrderDate>='2004-07-01'
       )

    EXCEPT

    Finalmente usando el nuevo comando Except.
    SELECT CustomerID
       FROM Sales.Customer
       WHERE TerritoryID=10
        EXCEPT
       SELECT customerid
       FROM Sales.SalesOrderHeader
       WHERE OrderDate>='2004-07-01'

    Diferentes maneras de escribir un query tipo INTERSECT

    Tres ejemplos:
    INNER JOIN
    Como un cliente puede tener mas de un pedido tengo que hacer una lista con distinct para los valores del customerid, veamos dos enfoques para lo mismo.

    As any customer can have more than one order I am going to have to make a distinct list of CustomerID values. I decided to try a couple of approaches.
    SELECT DISTINCT C.CustomerID
       FROM Sales.Customer AS C
        INNER JOIN Sales.SalesOrderHeader AS OH
        ON C.CustomerID = OH.CustomerID
       WHERE
        C.TerritoryID=10
        AND OH.OrderDate>='2004-07-01'
    
    SELECT C.CustomerID
       FROM Sales.Customer AS C
        INNER JOIN (SELECT DISTINCT CustomerID FROM Sales.SalesOrderHeader WHERE OrderDate>='2004-07-01'
       )AS OH
        ON C.CustomerID = OH.CustomerID
       WHERE C.TerritoryID=10
    WHERE CustomerID IN(…)
    SELECT CustomerID
       FROM Sales.Customer
       WHERE TerritoryID=10
       AND CustomerID IN(
        SELECT customerid
        FROM  Sales.SalesOrderHeader
        WHERE OrderDate>='2004-07-01'
       )
    INTERSECT
    Finalmente un intersect.
    SELECT CustomerID
       FROM Sales.Customer
       WHERE TerritoryID=10
        INTERSECT
       SELECT customerid
       FROM Sales.SalesOrderHeader
       WHERE OrderDate>='2004-07-01'
    The ANY and ALL Predicate
    ANY and ALL are predicates I have never needed to use.

    ANY

    Los dos queries nos ofrecen los mismos resultados y el mismo plan de ejecución:
    SELECT *
       FROM Sales.SalesPerson
       WHERE TerritoryID = ANY(
        SELECT TerritoryID FROM Sales.SalesTerritory WHERE CountryRegionCode='US'
       )
    
    SELECT *
       FROM Sales.SalesPerson
       WHERE TerritoryID IN(
        SELECT TerritoryID FROM Sales.SalesTerritory WHERE CountryRegionCode='US'
       )
       
    ALL
    All permite una comparación contra todos los valores en una lista de un select, por caso estos dos queries son idénticos:

    SELECT *
       FROM Sales.SalesOrderHeader
       WHERE TotalDue > ALL(SELECT TotalDue FROM Sales.TopSales)
       ORDER BY Sales.TotalDue DESC 
    
    SELECT *
       FROM Sales.SalesOrderHeader
       WHERE TotalDue > (SELECT MAX(TotalDue) FROM Sales.TopSales)
       ORDER BY Sales.TotalDue DESC 
    La página original se encuentra en:

    http://www.sqlservercentral.com/articles/T-SQL/67545/


    Hugo Román Bernachea
    Mail de contacto: SQLServer777@gmail.com

    Microsoft Certified DBA
    Microsoft Certified Trainer
    Twitter: @bernachea

    1 comentarios:

    Anónimo dijo...

    hola
    tengo un problema con except

    no puedo hacer que me devuelva las tuplas que quiero.

    el problema es que le entrego como tabla otra subconsulta (al lado izquierdo y derecho) y cuando trata de entregar la información busca las tablas que estan dentro de la subconsulta y toma como tabla, valga la redundancia, la misma subconsulta.

    Aqui va la query

    select *
    from (SELECT PTRALIN.ITEMS, SUM(CAST (PTRALIN.VALORNETO AS BIGINT )) AS SUMA
    FROM (SELECT PTRAL.SUBTITULO+PTRAL.ITEM+PTRAL.ASIGNACION AS ITEMS, *
    FROM indapRegion8.DBO.TEStptralin AS PTRAL) AS PTRALIN
    WHERE PTRALIN.CODTRAN='AE' AND PTRALIN.ITEMS!=0
    GROUP BY PTRALIN.ITEMS) as z
    except
    select *
    from (SELECT PTR_IN.ITEMS, PTR_AE.AE+PTR_IN.ING AS SUMA
    FROM (SELECT PTRALIN.ITEMS, SUM(CAST (PTRALIN.VALORNETO AS BIGINT )) AS ING
    FROM (SELECT PTRAL.SUBTITULO+PTRAL.ITEM+PTRAL.ASIGNACION AS ITEMS, *
    FROM indapRegion8.DBO.TEStptralin AS PTRAL) AS PTRALIN
    WHERE PTRALIN.CODTRAN='IN' AND PTRALIN.ITEMS!=0
    GROUP BY PTRALIN.ITEMS) AS PTR_IN
    JOIN (SELECT PTRALIN.ITEMS, SUM(CAST (PTRALIN.VALORNETO AS BIGINT )) AS AE
    FROM (SELECT PTRAL.SUBTITULO+PTRAL.ITEM+PTRAL.ASIGNACION AS ITEMS, *
    FROM indapRegion8.DBO.TEStptralin AS PTRAL) AS PTRALIN
    WHERE PTRALIN.CODTRAN='AE' AND PTRALIN.NUMEGRESO!='NULL' AND PTRALIN.ITEMS!=0
    GROUP BY PTRALIN.ITEMS) AS PTR_AE
    ON PTR_AE.ITEMS=PTR_IN.ITEMS) as y

    no se que otro operador utilizar en estos casos, porque lo que deseo es que me devuelva los datos de la tabla izquierda pero que no se encuentran en la tabla derecha.

    Publicar un comentario