2011-07-29 10 views
6

Esto parece tan básico, estoy estupefacto por la falta de una palabra mejor. Tengo dos tablas, llamémosles albums y artistsnulo e IN() proporciona resultados inesperados

CREATE TABLE `albums` (
    `album_id` bigint(20) NOT NULL AUTO_INCREMENT, 
    `artist_id` bigint(20) DEFAULT NULL, 
    `name` varchar(200) NOT NULL, 
    PRIMARY KEY (`album_id`) 
) 
CREATE TABLE `artists` (
    `artist_id` bigint(20) NOT NULL AUTO_INCREMENT, 
    `name` varchar(250) NOT NULL, 
    PRIMARY KEY (`artist_id`) 
) 

Hay unos cientos de miles reconds en cada mesa. Algunas de las filas del álbum tienen un nulo artist_id, esto es esperado.

Sin embargo, cuando realizo la siguiente consulta para encontrar artistas sin álbumes:

SELECT * FROM artists WHERE artist_id NOT IN (SELECT artist_id FROM albums)

... La consulta devuelve cero resultados. Sé que esto no es verdad. Así que traté de éste:

SELECT * FROM artists WHERE artist_id NOT IN (SELECT artist_id FROM albums WHERE artist_id IS NOT NULL)

... y regrese un par de miles de filas. Mi pregunta es: ¿por qué la primera consulta parece operar con la idea de que cualquier número = NULL? ¿O es este un efecto extraño que NULL tiene en la declaración IN()? Siento que esto es algo básico que me he perdido. Normalmente no uso NULL en mis tablas de db.

Respuesta

7

Esta es la razón por NOT EXISTS es semánticamente correcto

SELECT * FROM artists ar 
WHERE NOT EXISTS 
    (SELECT * FROM albums al WHERE ar.artist_id = al.artist_id) 

Lógica:

  • NOT IN (x, y, NULL) es en realidad
    • NOT (x OR y OR NULL) es en realidad
      • (NOT x) AND (NOT y) AND (NOT NULL)

Así NULL invalida la totalidad NOT IN

+0

Parece que NOT EXISTS fue marginalmente más rápido, también. ¡Muchas gracias por la información! –

7

Respuesta rápida: la declaración IN es un atajo para =a OR =b OR .... Si incluye nulos en esta lista, entonces creo que eso está rompiendo la declaración. Su segunda opción es probablemente una mejor opción.

O usar una unión también podría funcionar y ser más eficiente.

+0

true O unknown se evalúa como verdadero.Y es un NO EN: que se rompe de manera diferente – gbn

2

Tiene que ver con la forma de SQL NULL se interpretan - Tienes que pensar en ellos como valor desconocido.

Digamos que tiene artist_id = 1

Si ejecuta el siguiente:

artist_id = NULL 

En lugar de obtener un 'falso' - se obtiene 'desconocido';

Cuando ejecuta una consulta como la suya, solo se devuelven los valores que evalúan a 'TRUE'.

artist_id IN (NULL, NULL, NULL...) = UNKNOWN 
artist_id NOT IN (NULL, NULL, NULL....) = UNKNOWN 
+0

Bastante justo, aunque luego contestaría: ¿por qué usar nulo en absoluto? Si este fuera mi diseño de base de datos, habría usado 0 en lugar de nulo cuando no hay un artista. ¿Alguna razón convincente de por qué uno debería usar null en su lugar? –

+0

Es definitivamente discutible. Personalmente los uso como marcadores de posición convenientes: si estoy cargando una tabla que frecuentemente tiene muchos campos desconocidos, en lugar de tener que establecer siempre los valores en blanco (es decir, '', n/a, etc.), siempre me apego a NULL. Estoy contento con este método, aunque algunos considerarían esta mala práctica. – chris

+2

@Chris, algunas personas argumentan que el uso de valores de marcador de posición es más confuso, ya que cualquier persona que vea los datos debe saber qué valor es el marcador de posición. No puede ser una solución universal porque siempre habrá diseños en los que no se puede elegir un valor de marcador razonable. También existen beneficios prácticos en el uso de NULL: requiere menos almacenamiento tanto en tablas como en índices, y por lo tanto también puede aumentar el rendimiento. –

Cuestiones relacionadas