sql >> Base de Datos >  >> RDS >> Database

50 sombras de NULL:los diferentes significados de NULL en SQL

Tony Hoare, a quien se hace referencia principalmente como el inventor de la referencia NULL, ahora lo llama un error de mil millones de dólares que casi todos los lenguajes ahora "sufren", incluido SQL.

Citando a Tony (de su artículo de Wikipedia):

Yo lo llamo mi error de mil millones de dólares. Fue la invención de la referencia nula en 1965. En ese momento, estaba diseñando el primer sistema completo de tipos para referencias en un lenguaje orientado a objetos (ALGOL W). Mi objetivo era garantizar que todos los usos de las referencias fueran absolutamente seguros, con la verificación realizada automáticamente por el compilador. Pero no pude resistir la tentación de poner una referencia nula, simplemente porque era muy fácil de implementar. Esto ha llevado a innumerables errores, vulnerabilidades y bloqueos del sistema, que probablemente han causado mil millones de dólares en dolor y daños en los últimos cuarenta años.

Lo interesante aquí es que Tony estuvo tentado de implementar esa referencia porque era fácil de hacer. Pero, ¿por qué necesitaba esa referencia?

Los diferentes significados de NULL

En un mundo perfecto, no necesitaríamos NULL. Cada persona tiene un nombre y un apellido. Cada persona tiene una fecha de nacimiento, un trabajo, etc. ¿O no?

Desafortunadamente, no lo hacen.

No todos los países utilizan el concepto de nombre y apellido.

No todas las personas tienen trabajo. O a veces, no conocemos su trabajo. O no nos importa.

Aquí es donde NULL es extremadamente útil. NULL puede modelar todos estos estados que realmente no queremos modelar. NULL puede ser:

  • El valor "indefinido" , es decir, el valor que aún no está definido (probablemente por razones técnicas) pero que bien puede definirse más adelante. Piensa en una persona que queremos agregar a la base de datos para usarla en otras tablas. En una etapa posterior, agregaremos el trabajo de esa persona.
  • El valor "desconocido" , es decir, el valor que no conocemos (y puede que nunca sepamos). Tal vez ya no podamos preguntarle a esta persona o a sus familiares sobre su fecha de nacimiento:la información se perderá para siempre. Pero aún queremos modelar a la persona, por lo que usamos NULL en el sentido de DESCONOCIDO (que es su verdadero significado en SQL, como veremos más adelante).
  • El valor "opcional" , es decir, el valor que no necesita ser definido. Tenga en cuenta que el valor "opcional" también aparece en el caso de una UNIÓN EXTERNA, cuando la unión externa no produce ningún valor en un lado de la relación. O también cuando se utilizan CONJUNTOS DE AGRUPACIÓN, donde se combinan diferentes combinaciones de columnas GROUP BY (o se dejan vacías).
  • El valor "eliminado" o "evitado" , es decir, el valor que no queremos especificar. Quizás solemos registrar el estado civil de una persona como se hace en algunas jurisdicciones, pero no en otras, donde no es legal registrar ningún dato personal de este tipo. Por lo tanto, no queremos conocer este valor en algunos casos.
  • El valor "especial" en un contexto dado , es decir, el valor que no podemos modelar de otra manera en el rango de valores posibles. Esto se hace a menudo cuando se trabaja con intervalos de fechas. Supongamos que el trabajo de una persona está limitado por dos fechas, y si la persona está trabajando actualmente en ese puesto, usaremos NULL para indicar que el período no está limitado al final del intervalo de fechas.
  • El NULL “accidental” , es decir, el valor NULL que es simplemente NULL porque los desarrolladores no prestaron atención. En ausencia de una restricción NOT NULL explícita, la mayoría de las bases de datos asumen que las columnas admiten valores NULL. Y una vez que las columnas se pueden anular, los desarrolladores pueden simplemente "accidentalmente" poner valores NULL en sus filas, donde ni siquiera tenían la intención de hacerlo.

Como hemos visto anteriormente, estos son solo algunos de los 50 Shades of NULL .

El siguiente ejemplo muestra varios significados diferentes de NULL en un ejemplo SQL concreto:




CREATE TABLE company (
    id int NOT NULL,
    name text NOT NULL,
    CONSTRAINT company_pk PRIMARY KEY (id)
);
CREATE TABLE job (
    person_id int NOT NULL,
    start_date date NOT NULL,

    -- If end_date IS NULL, the “special value” of an unbounded
    -- interval is encoded
    end_date date NULL,
    description text NOT NULL,

    -- A job doesn’t have to be done at a company. It is “optional”.
    company_id int NULL,
    CONSTRAINT job_pk PRIMARY KEY (person_id,start_date),
    CONSTRAINT job_company FOREIGN KEY (company_id) 
        REFERENCES company (id) 
);
CREATE TABLE person (
    id int  NOT NULL,
    first_name text NOT NULL,

    -- Some people need to be created in the database before we
    -- know their last_names. It is “undefined”
    last_name text NULL,

    -- We may not know the date_of_birth. It is “unknown”
    date_of_birth date NULL,

    -- In some situations, we must not define any marital_status.
    -- It is “deleted”
    marital_status int NULL,
    CONSTRAINT person_pk PRIMARY KEY (id),
    CONSTRAINT job_person FOREIGN KEY (person_id)
        REFERENCES person (id)
); 

La gente siempre ha discutido sobre la ausencia de un valor

Cuando NULL es un valor tan útil, ¿por qué la gente sigue criticándolo?

Todos estos casos de uso anteriores para NULL (y otros) se muestran en esta interesante charla reciente de C.J. Date sobre "El problema de la información faltante" (vea el video en YouTube).

El SQL moderno puede hacer muchas cosas asombrosas que pocos desarrolladores de lenguajes de propósito general como Java, C#, PHP desconocen. Te mostraré un ejemplo más abajo.

En cierto modo, C.J. Date está de acuerdo con Tony Hoare en que (ab)usar NULL para todos estos tipos diferentes de "información faltante" es una muy mala elección.

Por ejemplo, en electrónica, se aplican técnicas similares para modelar cosas como 1, 0, "conflicto", "no asignado", "desconocido", "no importa", "alta impedancia". Sin embargo, observe cómo en la electrónica, diferentes valores especiales se utilizan para estas cosas, en lugar de un único valor NULL especial . ¿Es esto realmente mejor? ¿Cómo se sienten los programadores de JavaScript acerca de la distinción entre diferentes valores "falsos", como "nulo", "indefinido", "0", "NaN", la cadena vacía ""? ¿Es esto realmente mejor?

Hablando de cero:cuando dejamos el espacio de SQL por un momento y entramos en matemáticas, veremos que las culturas antiguas como los romanos o los griegos tenían los mismos problemas con el número cero. De hecho, ni siquiera tenían forma de representar el cero a diferencia de otras culturas, como se puede ver en el artículo de Wikipedia sobre el número cero. Citando del artículo:

Los registros muestran que los antiguos griegos parecían inseguros sobre el estado del cero como número. Se preguntaron a sí mismos:"¿Cómo puede nada ser algo?", lo que condujo a argumentos filosóficos y, en el período medieval, religiosos sobre la naturaleza y la existencia del cero y el vacío.

Como podemos ver, los “argumentos religiosos” se extienden claramente a la informática y el software, donde todavía no sabemos con seguridad qué hacer con la ausencia de un valor.

Volver a la realidad:NULL en SQL

Si bien las personas (incluidos los académicos) aún no están de acuerdo con el hecho de que necesitamos alguna codificación para "indefinido", "desconocido", "opcional", "eliminado", "especial", volvamos a la realidad y las partes malas de NULO de SQL.

Una cosa que se olvida con frecuencia cuando se trata de NULL de SQL es que implementa formalmente el caso DESCONOCIDO, que es un valor especial que forma parte de la llamada lógica de tres valores, y lo hace de manera inconsistente, p. en el caso de operaciones UNION o INTERSECT.

Si volvemos a nuestro modelo:





Si, por ejemplo, queremos encontrar a todas las personas que no están registradas como casadas, intuitivamente, nos gustaría escribir la siguiente declaración:

SELECT * FROM person WHERE marital_status != 'married'

Desafortunadamente, debido a la lógica de tres valores y al NULL de SQL, la consulta anterior no devolverá aquellos valores que no tengan un estado civil explícito. Por lo tanto, necesitaremos escribir un predicado explícito adicional:

SELECT * FROM person 
WHERE marital_status != 'married'
OR marital_status IS NULL

O forzamos el valor a algún valor NO NULO antes de compararlo

SELECT * FROM person
WHERE COALESCE(marital_status, 'null') != 'married'

La lógica de tres valores es difícil. Y no es el único problema con NULL en SQL. Aquí hay más desventajas de usar NULL:

  • Solo hay un NULL, cuando realmente queríamos codificar varios valores diferentes "ausentes" o "especiales". El rango de valores especiales útiles depende en gran medida del dominio y los tipos de datos que se utilizan. Sin embargo, siempre se requiere conocimiento del dominio para interpretar correctamente el significado de una columna anulable, y las consultas deben diseñarse cuidadosamente para evitar que se devuelvan resultados incorrectos, como vimos anteriormente.
  • Nuevamente, la lógica de tres valores es muy difícil de acertar. Si bien el ejemplo anterior sigue siendo bastante simple, ¿qué cree que arrojará la siguiente consulta?
    SELECT * FROM person 
    WHERE marital_status NOT IN ('married', NULL)
    

    Exactamente. No producirá nada en absoluto, como se explica en este artículo aquí. En resumen, la consulta anterior es la misma que la siguiente:

    SELECT * FROM person 
    WHERE marital_status != 'married'
    AND marital_status != NULL -- This is always NULL / UNKNOWN
    
  • La base de datos de Oracle trata NULL y la cadena vacía '' como la misma cosa. Esto es muy complicado ya que no notará de inmediato por qué la siguiente consulta siempre devuelve un resultado vacío:

    SELECT * FROM person 
    WHERE marital_status NOT IN ('married', '')
    

  • Oracle (nuevamente) no pone valores NULL en los índices. Esta es la fuente de muchos problemas de rendimiento desagradables, por ejemplo, cuando usa una columna anulable en un predicado NOT IN como tal:

    SELECT * FROM person 
    WHERE marital_status NOT IN (
      SELECT some_nullable_column
      FROM some_table
    )
    

    Con Oracle, la combinación anterior dará como resultado un escaneo completo de la tabla, independientemente de si tiene un índice en alguna_columna que admite valores nulos. Debido a la lógica de tres valores y a que Oracle no coloca valores NULL en los índices, el motor deberá presionar la tabla y verificar cada valor solo para asegurarse de que no haya al menos un valor NULL en el conjunto, lo que haría que el predicado completo DESCONOCIDO.

Conclusión

Todavía no hemos resuelto el problema NULL en la mayoría de los idiomas y plataformas. Si bien afirmo que NULL NO es el error de mil millones de dólares por el que Tony Hoare intenta disculparse, NULL tampoco está lejos de ser perfecto.

Si desea mantenerse seguro con el diseño de su base de datos, evite los NULL a toda costa, a menos que necesite absolutamente uno de esos valores especiales para codificar usando NULL. Recuerde, estos valores son:"indefinido", "desconocido", "opcional", "eliminado" y "especial", y más:Los 50 tonos de NULL . Si no se encuentra en tal situación, siempre agregue de manera predeterminada una restricción NOT NULL a cada columna en su base de datos. Su diseño será mucho más limpio y su rendimiento mucho mejor.

Si solo NOT NULL fuera el valor predeterminado en DDL y NULLABLE la palabra clave que debía establecerse explícitamente...

¿Cuáles son tus opiniones y experiencias con NULL? ¿Cómo funcionaría un mejor SQL en tu opinión?

Lukas Eder es fundador y director general de Data Geekery GmbH, con sede en Zúrich, Suiza. Data Geekery vende productos y servicios de bases de datos en torno a Java y SQL desde 2013.

Desde sus estudios de maestría en EPFL en 2006, ha estado fascinado por la interacción de Java y SQL. La mayor parte de esta experiencia la ha obtenido en el campo de la banca electrónica suiza a través de varias variantes (JDBC, Hibernate, principalmente con Oracle). Está feliz de compartir este conocimiento en varias conferencias, JUG, presentaciones internas y el blog de su empresa.