2012-02-02 11 views
21

Estaba leyendo sobre SALVO y se cruzan en la biblioteca de MSDN y me encontré con este ejemplo de cómo utilizar INTERSECT:¿Por qué existe EXCEPT en T-SQL?

USE AdventureWorks2008R2 GO 
SELECT ProductID 
FROM Production.Product 
INTERSECT 
SELECT ProductID 
FROM Production.WorkOrder ; 
--Result: 238 Rows (products that have work orders) 

Tal vez estoy pasado de moda, pero normalmente usaría el siguiente código para lograr el mismo resultado:

SELECT P.ProductID 
FROM Production.Product P 
INNER JOIN Production.WorkOrder W ON W.ProductID = P.ProductID 

¿Echo de falta algo, o soy INTERSECT lo mismo que INNER JOIN? ¿Hay algún beneficio en el rendimiento al usar uno sobre el otro?

La misma pregunta para EXCEPTO. ¿Cómo es esto:

USE AdventureWorks2008R2; 
GO 
SELECT ProductID 
FROM Production.Product 
EXCEPT 
SELECT ProductID 
FROM Production.WorkOrder ; 
--Result: 266 Rows (products without work orders) 

diferente de esto:

SELECT P.ProductID 
FROM Production.Product P 
LEFT JOIN Production.WorkOrder W ON W.ProductID = P.ProductID 
WHERE W.ProductID IS NULL 

?

+2

-1: No es así como funciona INTERSECT. Su primer ejemplo no devuelve productos que tienen órdenes de trabajo; devuelve el ID de cualquier producto que tenga el mismo ID que el ID de una orden de trabajo para un producto. –

Respuesta

22

Me voy a centrar en EXCEPT solo porque estoy más familiarizado con él. Además, como una exención de responsabilidad, mis ejemplos estarán en Sqlite, ya que estoy en una caja de Linux. Sin embargo, tanto Sqlite como SQL Server deberían ser compatibles con la funcionalidad.

Ambos INTERSECT y EXCEPT son operadores establecidos, que se derivan de las ideas subyacentes en relational algebra. Operan en valores distintos, siendo operadores configurados.

Su ejemplo es simplista. Daré un contraejemplo, usando una versión Sqlite de la base de datos de muestra Northwind.

Digamos que quiere obtener los CustomerID de todos los clientes que hicieron un pedido con EmployeeID de 5, pero NO aquellos que también hicieron un pedido con EmployeeID de 6. Esto es simple y natural con un EXCEPT.

SELECT CustomerID FROM orders 
WHERE EmployeeID = 5 
EXCEPT 
SELECT CustomerID FROM orders 
WHERE EmployeeID = 6 

Esto devuelve 14 filas en mi versión de Northwind.

Supongamos que decide volver a escribir esto usando JOIN s. Tal vez algo como esto?

SELECT o1.CustomerID 
FROM orders o1 INNER JOIN orders o2 ON o1.CustomerID = o2.CustomerID 
WHERE o1.EmployeeID = 5 AND o2.EmployeeID != 6 

Whoops, 525 rows. ¿Tal vez agregar un DISTINCT?

SELECT DISTINCT o1.CustomerID 
FROM orders o1 INNER JOIN orders o2 ON o1.CustomerID = o2.CustomerID 
WHERE o1.EmployeeID = 5 AND o2.EmployeeID != 6 

Ahora es 28 filas, siendo mucho más que lo que obtendríamos con EXCEPT.La razón es que esta no es la eliminación de CustomerIDs que han hecho un pedido con 6. Por el contrario, devuelve todos los CustomerIDs que tienen una orden con 5 y algunos employeeid otra de 6, ya sea o no que también tienen un pedido EmployeeID 6.

En resumen, EXCEPT y INTERSECT se establecen operadores que comparan dos consultas, devuelven tuplas únicas, y ciertamente tienen su uso.

+3

Puede escribir código como este selecto o1.CustomerId de ( seleccione CustomerId de pedidos en los que EmployeeID = 5 ) como o1 externa izquierda ( seleccione CustomerID de pedidos en los que EmployeeID = 6 ) como O2 en o1. CustomerID = o2.CustomerID donde o2.CustomerID es nulo – danmiao

0

En mi opinión EXCEPT y INTERSECT se utilizan para hacer las mismas cosas que el comando JOIN, pero es más simple con tablas que no tienen claves primarias, por ejemplo:

con INTERSECT:

SELECT FIRSTNAME, 
     LASTNAME, 
     ADDRESSLINE1, 
     CITY, 
     STATEPROVINCECODE, 
     POSTALCODE 
FROM MANAGER 
EXCEPT 
SELECT FIRSTNAME, 
     LASTNAME, 
     ADDRESSLINE1, 
     CITY, 
     STATEPROVINCECODE, 
     POSTALCODE 
FROM CUSTOMER 

Y tener los mismos resultados con JOIN, que debe hacer:

SELECT M.FIRSTNAME, 
     M.LASTNAME, 
     M.ADDRESSLINE1, 
     M.CITY, 
     M.STATEPROVINCECODE, 
     M.POSTALCODE 
FROM  MANAGER M 
WHERE NOT EXISTS (SELECT * 
        FROM CUSTOMER C 
        WHERE M.FIRSTNAME = C.FIRSTNAME 
          AND M.LASTNAME = C.LASTNAME 
          AND M.ADDRESSLINE1 = C.ADDRESSLINE1 
          AND M.CITY = C.CITY 
          AND M.POSTALCODE = C.POSTALCODE) 
GROUP BY M.FIRSTNAME,M.LASTNAME,M.ADDRESSLINE1,M.CITY, 
     M.STATEPROVINCECODE,M.POSTALCODE 

Más información here.

1

Sus ejemplos de sus consultas "equivalentes" están equivocados - la consulta con INTERSECT no siempre devuelven el mismo resultado que INNER JOIN y lo mismo para EXCEPT y LEFT JOIN.

Look en particular el ejemplo sobre INTERSECT:

DECLARE @t TABLE(t INT NOT NULL) 
DECLARE @x TABLE(x INT NOT NULL) 

INSERT @t 
VALUES (1), (2), (3) 

INSERT @x VALUES(1), (1), (1) 

SELECT t FROM @t 
INTERSECT SELECT x FROM @x 

SELECT t FROM @t 
INNER JOIN @x ON x = t 

INTERSECT es más como (pero no el mismo) como IN cláusula:

SELECT t FROM @t 
WHERE t IN (select x FROM @x) 

o como EXISTS

SELECT t FROM @t 
WHERE EXISTS (select * FROM @x WHERE x = t) 

Los mismos ejemplos que puede adaptar a la cláusula EXCEPT.

+0

Usted es correcto, pero vea mi respuesta a la teoría detrás de él ... – gbn

19
  • INTERSECT y EXCEPT son semi-une
  • JOIN es equicombinación a

Así que cuando se une a 2 mesas que coinciden con, digamos, 5 filas y 3 filas

  • JOIN da 15 filas
  • INTERSECT da 3 filas

SALVO es similar a la combinación externa por la misma razón

Ya que estamos hablando de semi-une, a continuación, en su mayoría

  • INTERSECT le da los mismos resultados como el que existe
  • SALVO da la mismos resultados que existe no

La "mayoría" viene porque ambos se cruzan y SALVO

Editar, demostración rápida de todo esto

DECLARE @t1 TABLE (t1col INT); 
INSERT @t1 VALUES (1), (2), (2), (3), (3), (5), (5); 

DECLARE @t2 TABLE (t2col INT); 
INSERT @t2 VALUES (1), (2), (3), (4); 

SELECT 'INNER JOIN', * FROM @t1 t1 JOIN @t2 t2 ON t1.t1col = t2.t2col -- same both ways 

SELECT 't1 INTERSECT t2', * FROM @t1 INTERSECT SELECT 't1 INTERSECT t2', * FROM @t2; 

SELECT 't2 INTERSECT t1', * FROM @t2 INTERSECT SELECT 't2 INTERSECT t1', * FROM @t1; 

SELECT 't1 EXISTS t2', * FROM @t1 t1 
WHERE EXISTS (SELECT * FROM @t2 t2 WHERE t1.t1col = t2.t2col); 

SELECT 't2 EXISTS t1', * FROM @t2 t2 
WHERE EXISTS (SELECT * FROM @t1 t1 WHERE t1.t1col = t2.t2col); 

SELECT 't1 LEFT JOIN t2, IS NULL', * FROM @t1 t1 LEFT JOIN @t2 t2 ON t1.t1col = t2.t2col WHERE t2.t2col IS NULL 
SELECT 't2 LEFT JOIN t1, IS NULL', * FROM @t2 t2 LEFT JOIN @t1 t1 ON t1.t1col = t2.t2col WHERE t1.t1col IS NULL 

SELECT 't1 EXCEPT t2', * FROM @t1 EXCEPT SELECT 't1 EXCEPT t2', * FROM @t2; 

SELECT 't2 EXCEPT t1', * FROM @t2 EXCEPT SELECT 't2 EXCEPT t1', * FROM @t1; 

SELECT 't1 NOT EXISTS t2', * FROM @t1 t1 
WHERE NOT EXISTS (SELECT * FROM @t2 t2 WHERE t1.t1col = t2.t2col); 

SELECT 't2 NOT EXISTS t1', * FROM @t2 t2 
WHERE NOT EXISTS (SELECT * FROM @t1 t1 WHERE t1.t1col = t2.t2col); 

actualización: Feb de 2013.Se agregó una columna adicional para describir la operación

+0

1 Integral! –

+1

@Mikael Eriksson: Estoy equivocado, reparado. No tenía un buen guión de demostración suficiente antes – gbn

+0

En realidad, mirando hacia atrás, no se 'EXCEPT' un [antijoin] (http://en.wikipedia.org/wiki/Relational_algebra#Joins_and_join-like_operators), no un Semiunión ? – voithos