2009-05-13 18 views
16

que fue depurando un procedimiento almacenado, el otro día y se encontró algo de lógica algo como esto:"<>" vs "no en"

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

Este regresó nada. Pensé que se veía un poco extraño con el "<>", así que lo cambié a "NO ENCENDIDO" y luego todo funcionó bien. Me preguntaba por qué es esto? Este es un proceso bastante antiguo y no estoy seguro de cuánto tiempo ha pasado el problema, pero recientemente cambiamos de SQL Server 2005 a SQL Server 2008 cuando se descubrió esto. ¿Cuál es la diferencia real entre "<>" y "NOT IN" y ha cambiado el comportamiento entre Server2005 y 2008?

Respuesta

18
SELECT something 
FROM someTable 
WHERE idcode NOT IN (SELECT ids FROM tmpIdTable) 

verifica cualquier valor en la lista.

Sin embargo, el NOT IN no es tolerantes a NULL. Si la subconsulta arrojaba un conjunto de valores que contenía NULL, no se devolverían registros en absoluto. (Esto es porque internamente el NOT IN está optimizado para idcode <> 'foo' AND idcode <> 'bar' AND idcode <> NULL etc., que siempre fallará porque cualquier comparación con NULL produce UNKNOWN, evitando que la expresión completa se vuelva VERDADERO.)

Una variante más tolerante a NULL sería esto:

SELECT something 
FROM someTable 
WHERE NOT EXISTS (SELECT ids FROM tmpIdTable WHERE ids = someTable.idcode) 

EDIT: I inicialmente supone que esta:

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

comprobaría contra sólo el primer valor. Resulta que esta suposición es incorrecta, al menos para SQL Server, donde realmente desencadena su error:

Msg 512, Level 16, State 1, Line 1 
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. 
+1

'WHERE IDcode NOT IN (...)' es equivalente a 'WHERE idcode <> ALL (...) ' –

7

<> es una operación "singular" NOT; NOT IN es una operación de conjunto, por lo que tiene sentido que la primera no funcione. No obstante, no tengo idea de si pudo o no haberlo hecho en una versión anterior de SQL Server.

+0

Nunca se ha hecho nada diferente en SQL Server. – Tomalak

11

probar esto, puede correr más rápido debido al uso del índice:

SELECT something 
FROM someTable 
    LEFT OUTER JOIN tmpIdTable ON idcode=ids 
WHERE ids IS NULL 
+0

+1 buena alternativa –

+0

¡Es una buena idea! –

+0

SQL Server normalmente (no he visto un caso cuando no lo ha hecho) produce QEP idénticos para una combinación o una subconsulta y, por lo tanto, un rendimiento idéntico. – pipTheGeek

2

no tengo idea ¿Por qué escribirías algo como WHERE idcode <> (SELECT ids FROM tmpIdTable)? Una instrucción SELECT devolverá un conjunto de tuplas y su idcode ya sea o NO estará EN ESTE conjunto. "WHERE idcode NOT IN (SELECT ids FROM tmpIdTable)" es la manera de hacerlo.

+0

Por eso sabía arreglarlo, simplemente no sabía exactamente cuál era la lógica detrás de eso. –

+0

La instrucción <> puede tener sentido si la subconsulta está ordenada de alguna manera, p. "<> la mayor [cosa] en la lista". – Tomalak

+0

Tiene razón, en caso de que se solicite la subconsulta, la notación <> podría tener algún sentido, pero lo evitaría de todos modos, ya que es propenso a errores, es fácil pasar por alto su intención.Quien remueva o cambie el orden de la subconsulta más tarde puede quedar desconcertado por el hecho de que la consulta no devuelva nada. –

-1

en algunas versiones de SQL ! = se debe utilizar para una declaración lógica "no igual". ¿Has probado eso?

+2

'<>' y '! =' Son equivalentes en SQL Server. No hay una versión que insista en '! ='. Pero '<>' es la forma estándar de ANSI SQL de hacerlo, aunque personalmente tiendo a usar! = Más a menudo por alguna razón. – Tomalak

4

Este código es válido si y sólo si no hay filas o de una sola fila de regresar de tmpIdTable:

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

Si se devuelven varias filas, recibirá un error como:

Msg 512 , Nivel 16, estado 1, línea 1 La subconsulta arrojó más de 1 valor. Esto no está permitido cuando la subconsulta sigue =,! =, <, < =,>,> = o cuando la subconsulta se usa como una expresión.

Este es el mismo error que se obtiene con escalar anidada inesperadamente produce múltiples filas como:

SELECT *, (SELECT FROM t1 bla DONDE etc.) desde la T2

Nada ha cambiado WRT esto en SQL Server en una década, así que espero que las suposiciones sobre la consulta anidada en el código original se hayan roto.

Si no se devuelven las filas, el resultado estará vacío ya que <> NULL nunca es verdadero (suponga ANSI NULLs).

Este código es válido para cualquier número de filas:

SELECT something 
FROM someTable 
WHERE idcode NOT IN (SELECT ids FROM tmpIdTable) 

Sin embargo, todavía puede haber problemas con NULL.

+0

De hecho, tuve esta misma conversación esta mañana con un compañero de trabajo. Es similar, pero no del todo la respuesta. –

+0

Conceptualmente, al menos, incluso está mal si la subselección arroja cero o una fila. Porque estás preguntando si un escalar, idcode, es igual a una lista de cero o de un elemento. El escalar nunca es realmente igual a la lista, incluso si es igual al único elemento en una lista de un elemento. –

+0

De hecho, nunca uso el constructo y normalmente lo consideraría un olor a código. –

2

Si la subconsulta SELECT devuelve cero filas, eso es NULO. Cuando se compara NULL con algo, el resultado siempre es UNKNOWN, y nunca es TRUE. De manera bastante confusa, NO DESCONOCIDO es igual a DESCONOCIDO.

Evito la lógica de tres valores (VERDADERO, FALSO, DESCONOCIDO) siempre que sea posible. No es tan difícil de evitar una vez que lo dominas.

Si la subconsulta SELECT devuelve exactamente un valor, la comparación de desigualdad debe devolver el resultado esperado.

Si la subconsulta SELECT devuelve más de un valor, debería recibir un error.

En general, NOT IN devolverá el resultado que espera cuando prueba para no ser miembro de un conjunto.

Esta respuesta se superpone a otras respuestas, pero está redactada de forma un poco diferente.

Editado para añadir más detalles acerca NO EN:

hice algunas búsquedas sobre NO EN en Oracle, y he aprendido algo que no sabía hace media hora. NOT IN es NULL sensible. En particular,

X NOT IN (SELECT ...) 

No es lo mismo que

NOT (X IN SELECT ...)) 

voy a tener que modificar mi respuesta anterior!

+0

+1 para señalar la parte TRUE/FALSE/UNKNOWN. Gracias. – Tomalak