1. Qué es
NULL no es cero. No es cadena vacía. No es "falso". NULL significa desconocido.
Y de ahí sale todo lo demás: si un valor es desconocido, cualquier comparación con él es también desconocida. No verdadera, no falsa: desconocida. SQL tiene tres valores lógicos, no dos:
TRUE · FALSE · UNKNOWN
WHERE solo deja pasar TRUE. FALSE y UNKNOWN se descartan igual — y esa es la fuente de casi todos los resultados silenciosamente incompletos que verás en tu vida.
En Northwind hay NULL por todas partes y significan cosas distintas:
| Columna | NULL significa |
|---|---|
customers.region |
El país no usa regiones (60 de 91 clientes) |
orders.shipped_date |
Todavía no se ha enviado (21 pedidos) |
employees.reports_to |
No tiene jefe — es el director |
Fíjate: en un caso es "no aplica", en otro "aún no", y en otro "no existe". SQL usa el mismo NULL para los tres.* Interpretarlo es tu trabajo, no del motor.
Antes de seguir, una sola pregunta: ¿alguna columna que voy a usar puede tener nulos?
Todo lo que sigue en este tema tiene una sola causa: una columna que participa en la consulta admite NULL. Da igual dónde participe — en una expresión del SELECT, dentro de una agregación, en una condición del WHERE, en el ORDER BY.
Si ninguna lo admite, cada valor existe y cada comparación es cierta o falsa, y no hay nada que pensar. Así que antes de escribir la consulta, mira si las columnas que vas a tocar admiten nulos: en DBeaver, doble clic en la tabla → pestaña Columns → columna Not Null; en psql, \d orders. Es lo mismo que hiciste en leer-un-esquema-desconocido.
Y ojo con la trampa: "admite nulos" no es lo mismo que "tiene nulos". Una columna con nulos a la vista es manejable — los ves, los cuentas, decides qué hacer con ellos. La peligrosa es la que admite nulos y hoy no tiene ninguno: tu consulta funciona, la das por buena, y el día que alguien inserta la primera fila con ese hueco el resultado cambia sin que nadie toque la consulta. Lo que te protege es la restricción NOT NULL en el esquema, no el hecho de que hoy no haya ninguno.
2. Sintaxis
columna IS NULL
columna IS NOT NULL
columna IS DISTINCT FROM valor -- comparación que sí trata NULL como un valor
columna IS NOT DISTINCT FROM valor
coalesce(a, b, c) -- el primero que no sea NULL
nullif(a, b) -- NULL si a = b; si no, a
= NULL no da error y nunca es cierto. Ese es el problema: no avisa.
3. Ejemplos sobre Northwind
El error más común, con datos reales:
SELECT
count(*)
FROM
customers
WHERE
region = NULL; -- 0
SELECT
count(*)
FROM
customers
WHERE
region IS NULL; -- 60
La primera no da error. Devuelve cero y parece una respuesta. Pero region = NULL es "¿es este valor igual a un desconocido?", y eso no se puede saber: no es t ni f, es NULL, y el WHERE lo descarta en las 91 filas. IS NULL es el único operador que pregunta por la ausencia sin caer en la trampa.
Contar nulos:
SELECT
count(*) AS filas,
count(region) AS con_region,
count(*) - count(region) AS nulos
FROM
customers;
filas | con_region | nulos
-------+------------+-------
91 | 31 | 60
count(*) cuenta filas. count(columna) cuenta valores no nulos. Restarlos es la forma más corta de auditar una columna, y conviene hacerlo con toda columna nueva antes de fiarse de ella.
3.1 En el SELECT: la aritmética y el texto se contagian
SELECT
'Lima-' || NULL AS concatenado,
10 + NULL AS suma;
concatenado | suma
-------------+------
|
Ambas dan NULL. Un solo NULL en una expresión la vuelve NULL entera. Con datos reales se ve el daño: construye la dirección completa de cada cliente juntando calle, ciudad, región y país.
SELECT
company_name,
address,
city,
region,
country,
address || ', ' || city || ', ' || region || ', ' || country AS direccion
FROM
customers
ORDER BY
customer_id
LIMIT 3;
company_name | address | city | region | country | direccion
------------------------------------+-------------------------------+-------------+--------+---------+-----------
Alfreds Futterkiste | Obere Str. 57 | Berlin | | Germany |
Ana Trujillo Emparedados y helados | Avda. de la Constitución 2222 | México D.F. | | Mexico |
Antonio Moreno Taquería | Mataderos 2312 | México D.F. | | Mexico |
Alfreds Futterkiste tiene calle, ciudad y país. Solo le falta la región, que en Alemania no se usa. Y su dirección sale vacía: tres datos válidos perdidos por uno que ni siquiera hacía falta. Te quedas sin dirección, no sin región. A escala, de 91 clientes con calle, solo 31 tienen dirección; los otros 60 salen en blanco, y la consulta no avisa.
Dos formas de evitarlo. La primera protege cada pieza con coalesce (se explica en 3.5): si vale NULL, se sustituye por un texto que tú eliges. Y se protegen las cuatro, no solo region: hoy es la única con nulos, pero en customers las cuatro columnas los admiten, y lo que hoy es un dato mañana puede no serlo. Fíjate que el coalesce envuelve ', ' || city entero, no solo city, y que el texto de relleno lleva su propia coma: así la puntuación sale bien tanto si la pieza existe como si no.
SELECT
company_name,
address,
city,
region,
country,
coalesce(address, 's/n calle')
|| coalesce(', ' || city, ', s/n ciudad')
|| coalesce(', ' || region, ', s/n región')
|| coalesce(', ' || country, ', s/n país') AS direccion
FROM
customers
ORDER BY
customer_id
LIMIT 3;
company_name | address | city | region | country | direccion
------------------------------------+-------------------------------+-------------+--------+---------+----------------------------------------------------------------
Alfreds Futterkiste | Obere Str. 57 | Berlin | | Germany | Obere Str. 57, Berlin, s/n región, Germany
Ana Trujillo Emparedados y helados | Avda. de la Constitución 2222 | México D.F. | | Mexico | Avda. de la Constitución 2222, México D.F., s/n región, Mexico
Antonio Moreno Taquería | Mataderos 2312 | México D.F. | | Mexico | Mataderos 2312, México D.F., s/n región, Mexico
La segunda usa concat_ws (with separator): pone el separador solo entre las piezas que existen y salta los NULL sin que tengas que protegerlas una a una.
SELECT
company_name,
address,
city,
region,
country,
concat_ws(', ', address, city, region, country) AS direccion
FROM
customers
ORDER BY
customer_id
LIMIT 3;
company_name | address | city | region | country | direccion
------------------------------------+-------------------------------+-------------+--------+---------+----------------------------------------------------
Alfreds Futterkiste | Obere Str. 57 | Berlin | | Germany | Obere Str. 57, Berlin, Germany
Ana Trujillo Emparedados y helados | Avda. de la Constitución 2222 | México D.F. | | Mexico | Avda. de la Constitución 2222, México D.F., Mexico
Antonio Moreno Taquería | Mataderos 2312 | México D.F. | | Mexico | Mataderos 2312, México D.F., Mexico
Compara las dos columnas direccion: con coalesce dices qué va en el hueco (s/n región); con concat_ws el hueco desaparece. Esa es la diferencia, y por eso ninguna es mejor que la otra en abstracto. Si el que lee la etiqueta necesita saber que faltó algo, coalesce. Si solo quiere una dirección limpia, concat_ws. Ninguna de las dos inventa lo que falta: un cliente sin calle sigue sin calle, y ese paquete tampoco llega. Antes de concatenar, cuenta los nulos de cada pieza.
3.2 En las agregaciones: lo ignoran
SELECT
count(*) AS filas,
count(x) AS no_nulos,
sum(x) AS suma,
avg(x) AS media,
avg(coalesce(x, 0)) AS media_nulos_como_cero
FROM
(VALUES (10), (20), (NULL)) t(x);
filas | no_nulos | suma | media | media_nulos_como_cero
-------+----------+------+---------------------+-----------------------
3 | 2 | 30 | 15.0000000000000000 | 10.0000000000000000
La media es 15, no 10. avg sumó 30 y dividió entre 2, no entre 3: los NULL no entran en el denominador. Casi siempre es lo que quieres. Casi. Si esos nulos significaban "cero ventas", tu media está inflada y nadie te avisará.
La última columna es la salida cuando el nulo sí significa cero: convertirlo antes de que llegue a avg. coalesce(x, 0) se lee "x, y si x es NULL, 0". Se evalúa fila a fila: en las dos primeras gana x (10 y 20); en la tercera x es NULL y se pasa al siguiente candidato, 0. Así avg recibe 10, 20, 0 y divide entre 3. El coalesce va dentro, sobre el valor que puede faltar — coalesce(avg(x), 0) no serviría, porque para cuando avg termina el nulo ya fue ignorado y la media ya es 15.
Pero fíjate en que la decisión no es técnica: tú tienes que saber qué significa ese nulo. Si es "no vendió nada", avg(coalesce(x, 0)). Si es "no reportó", avg(x) es la correcta, y el coalesce te metería ceros inventados en el denominador. El motor no puede distinguirlo; es la sección 1 otra vez.
3.3 En el WHERE: la fila se va sin avisar
El WHERE solo deja pasar TRUE. Cuando una condición da UNKNOWN, la fila se va, y no avisa. En un listado a veces se nota; en un recuento, nunca: un número no enseña qué filas le faltan.
Tres preguntas de negocio sobre los pedidos a Francia. Tiene 77, y 2 de ellos tienen shipped_date en NULL.
"¿Cuántos pedidos son de Francia y llegaron con retraso?"
SELECT
count(*) AS con_retraso
FROM
orders
WHERE
ship_country = 'France'
AND shipped_date > required_date; -- 3
Sigue lo que hace el motor fila por fila. Francia tiene 77 pedidos. En 75 de ellos, shipped_date tiene valor: la comparación shipped_date > required_date se evalúa con normalidad, da TRUE o FALSE, y el filtro se aplica — 3 pasan. En los otros 2, shipped_date es NULL: la comparación no da TRUE ni FALSE, da UNKNOWN, y el WHERE los descarta sin haberlos evaluado realmente. No es que el motor haya decidido que no llegaron tarde; es que no pudo decidir nada y los tiró.
Y fíjate en la otra columna de la comparación. required_date también admite NULL — hoy no tiene ninguno, pero el esquema lo permite. Si mañana entra un pedido sin fecha requerida, shipped_date > required_date da UNKNOWN por el otro lado, y ese pedido desaparece igual: un pedido del que no puedes saber si llegó tarde porque no tienes contra qué compararlo, y tu consulta lo quita de una sin que cambies una letra. Es el caso del paso cero: la columna peligrosa no es la que tiene nulos a la vista, sino la que los admite y hoy no los tiene.
La regla, en dos pasos. El 3 no está mal. Lo que no es confiable es el 3 solo. Cuando la pregunta toca columnas que admiten nulos, hay que hacer dos cosas, y son dos personas distintas las que las hacen:
Paso 1 — quien escribe la consulta: decir en la consulta qué se descarta.
SELECT
count(*) AS con_retraso
FROM
orders
WHERE
ship_country = 'France'
AND shipped_date IS NOT NULL
AND required_date IS NOT NULL
AND shipped_date > required_date; -- 3
Esos dos IS NOT NULL no cambian el número: el motor ya descartaba esas filas. Lo que cambian es que la consulta ahora dice en voz alta "solo cuento los que tienen las dos fechas", en vez de hacerlo en silencio. Es la diferencia entre una exclusión que decidiste y una que te pasó. Quien lea esta consulta dentro de un año —tú, probablemente— sabrá qué está midiendo.
Paso 2 — quien responde la pregunta: decir cuántas filas no se pudieron evaluar, y por qué.
Se cuentan con la misma condición que las excluyó, al revés: las que tienen nulo en cualquiera de las columnas que participan en la comparación.
SELECT
count(*) AS no_evaluables
FROM
orders
WHERE
ship_country = 'France'
AND (shipped_date IS NULL OR required_date IS NULL); -- 2
Y la respuesta no es solo "3" del paso 1. Es: "de los 77 pedidos a Francia, 3 llegaron con retraso. Hay 2 que no se pueden evaluar porque tienen nulos en alguna de las columnas de la condición." Ese enunciado no se puede malinterpretar: dice lo que se midió, sobre cuántos, y cuántos quedaron fuera y por qué.
El paso 1 protege la consulta; el paso 2 protege la respuesta. Ninguno sustituye al otro.
"¿Y cuántos llegaron sin retraso?" — lo que pasa cuando no se hace ninguno de los dos.
SELECT
count(*) AS sin_retraso
FROM
orders
WHERE
ship_country = 'France'
AND NOT (shipped_date > required_date); -- 72
72 parece normal, y está mal. 3 con retraso + 72 sin retraso = 75. Francia tiene 77. Los 2 sin fecha tampoco están aquí: NOT UNKNOWN sigue siendo UNKNOWN. No están en "con retraso" ni en "sin retraso" — están en el limbo. Si esas dos cifras van a un gráfico, no suman el total; si calculas el porcentaje de retraso como 3/75 en vez de 3/77, sale inflado.
Con los dos pasos se arregla solo. Paso 1: la consulta dice que excluye los nulos (AND shipped_date IS NOT NULL AND required_date IS NOT NULL), y el 72 pasa a significar "sin retraso entre los que se pueden evaluar". Paso 2: la respuesta es "de los 77 pedidos a Francia, 72 llegaron sin retraso y 3 con retraso. Hay 2 que no se pueden evaluar porque tienen nulos en alguna de las columnas de la condición." Y ahora 72 + 3 + 2 = 77: las partes suman el total. Esa suma es la comprobación más barata que existe, y cuando no cuadra, la causa son los nulos.
"¿Cuántos pedidos son de Francia o tienen retraso?" — el caso en que el nulo no descarta, sino que cuela.
Ojo: esta pregunta ya no es dentro de Francia. Un pedido de Brasil con retraso también cuenta, así que se evalúa sobre los 830 pedidos de la tabla. Y en la tabla hay 21 sin shipped_date: los 2 de Francia y 19 de otros países.
SELECT
count(*) AS francia_o_retraso
FROM
orders
WHERE
ship_country = 'France'
OR shipped_date > required_date; -- 111
Los 2 de Francia sin fecha entran, porque ship_country = 'France' es TRUE y a un OR le basta un lado cierto. Los 19 sin fecha de otros países se van, porque FALSE OR UNKNOWN es UNKNOWN. Los 21 tienen el mismo problema —no se sabe si llegaron con retraso— y el OR metió a unos y sacó a otros. El 111 mezcla lo que se evaluó con lo que se coló.
Los dos pasos, otra vez. Paso 1:
SELECT
count(*) AS francia_o_retraso
FROM
orders
WHERE
shipped_date IS NOT NULL
AND required_date IS NOT NULL
AND (ship_country = 'France' OR shipped_date > required_date); -- 109
Ahora la exclusión está escrita, es la misma para todos los pedidos, y el número cambió: 109, no 111. Paso 2: las no evaluables, con la misma condición al revés (shipped_date IS NULL OR required_date IS NULL), son 21. Y la respuesta: "109 pedidos son de Francia o llegaron con retraso. Hay 21 que no se pueden evaluar porque tienen nulos en alguna de las columnas de la condición."
Esta es la razón de que la regla sea de dos pasos y no de uno: en la primera pregunta el nulo descartaba y el número salía bien por casualidad; aquí el nulo cuela y el número sale mal. No puedes saber de antemano hacia qué lado va a fallar. Los IS NOT NULL explícitos hacen que dé igual.
3.4 En el WHERE: la trampa grande, NOT IN
En where-y-operadores viste IN como la forma legible del OR: country IN ('Austria', 'Ireland', 'Norway'). NOT IN es lo contrario, y se escribe igual.
Pregunta: ¿cuántos clientes tenemos fuera de Francia, Alemania y Brasil?
SELECT
count(*) AS fuera_de_la_lista
FROM
customers
WHERE
country NOT IN ('France', 'Germany', 'Brazil'); -- 60
Ahora la misma lista con un NULL dentro:
SELECT
count(*) AS fuera_de_la_lista
FROM
customers
WHERE
country NOT IN ('France', 'Germany', 'Brazil', NULL); -- 0
Cero. Un solo NULL en la lista y no sobrevive ninguna fila, sea del país que sea.
El motivo es cómo se traduce NOT IN. Si x IN (a, b, c) es x = a OR x = b OR x = c, entonces x NOT IN (a, b, c) es lo contrario, con AND:
country <> 'France' AND country <> 'Germany' AND country <> 'Brazil' AND country <> NULL
Y country <> NULL es UNKNOWN para todas las filas, sea cual sea el país. TRUE AND UNKNOWN = UNKNOWN, no pasa el WHERE, y como le pasa a todas, no queda ninguna.
Compáralo con IN, que con el mismo NULL funciona:
SELECT
count(*) AS en_la_lista
FROM
customers
WHERE
country IN ('France', 'Germany', 'Brazil', NULL); -- 31
IN es una cadena de OR, y a un OR le basta con que un lado sea cierto: country = 'France' ya decide, el NULL sobra. NOT IN es una cadena de AND, y a un AND le basta un desconocido para no llegar nunca a TRUE. Es la tabla de verdad de 3.3 otra vez.
¿Y quién pone un NULL en una lista a mano? Nadie. El NULL llega cuando la lista no la escribes tú: viene de otra consulta (se ve en el bloque 6, subconsultas-escalares-y-multivalor) o de una aplicación que pasa los valores como parámetros. En ese momento no lo ves, y la consulta que ayer devolvía 60 hoy devuelve 0.
Regla:
NOT INes una bomba de relojería si la lista puede traer unNULL. Si la lista sale de otra consulta, usaNOT EXISTS(bloque 5, anti-join-y-semi-join).
3.5 La herramienta: coalesce y nullif
SELECT
company_name,
region,
coalesce(region, 'SIN REGION') AS region_limpia
FROM
customers
LIMIT 4;
company_name | region | region_limpia
------------------------------------+--------+---------------
Alfreds Futterkiste | | SIN REGION
Ana Trujillo Emparedados y helados | | SIN REGION
coalesce recibe una lista de candidatos —columnas, valores, los que quieras— y devuelve el primero que no sea nulo. Los recorre en orden: si el primero es NULL, mira el segundo; si también, el tercero; y así hasta encontrar uno con valor.
coalesce frente a ifnull / ISNULL. Si vienes de MySQL conoces ifnull(a, b); si vienes de SQL Server, ISNULL(a, b). Evalúan cada candidato igual que coalesce, con una sola diferencia: su lista es de exactamente dos. coalesce no tiene tope: puedes darle tres columnas, cinco, las que necesites. Y en PostgreSQL ifnull e ISNULL no existen — solo hay coalesce.
Esa diferencia se nota en cuanto la lista pasa de dos. Para confirmar un pedido, prefiero el fax; si no tiene, el teléfono; si tampoco, que lo diga.
SELECT
company_name,
fax,
phone,
coalesce(fax, phone, 'sin contacto') AS contacto
FROM
customers
ORDER BY
customer_id
LIMIT 3;
company_name | fax | phone | contacto
-------------------------+-------------+----------------+----------------
Alfreds Futterkiste | 030-0076545 | 030-0074321 | 030-0076545
Antonio Moreno Taquería | | (5) 555-3932 | (5) 555-3932
B's Beverages | | (171) 555-1212 | (171) 555-1212
Una lista de tres —dos columnas y un texto— en una sola llamada. Con ifnull solo caben dos, así que habría que anidar: ifnull(ifnull(fax, phone), 'sin contacto'), y con cuatro candidatos, tres niveles. coalesce es la versión estándar, funciona en todos los motores y su lista no tiene tope: no hay motivo para usar la otra.
nullif es el inverso — convierte un valor en NULL:
SELECT
nullif(10, 10) AS a,
nullif(10, 5) AS b;
a | b
---+----
| 10
Su uso real es evitar la división por cero. ¿Qué porcentaje del stock de cada producto está ya pedido?
SELECT
product_name,
units_on_order,
units_in_stock,
100 * units_on_order / units_in_stock AS pct_pedido
FROM
products;
ERROR: division by zero
Revienta entera: hay productos con units_in_stock en 0, y basta uno para que la consulta no devuelva nada. Con nullif el cero pasa a NULL antes de dividir, y dividir entre NULL no da error, da NULL:
SELECT
product_name,
units_on_order,
units_in_stock,
100 * units_on_order / nullif(units_in_stock, 0) AS pct_pedido
FROM
products
ORDER BY
product_id
LIMIT 3;
product_name | units_on_order | units_in_stock | pct_pedido
------------------------+----------------+----------------+------------
Chai | 0 | 39 | 0
Chef Anton's Gumbo Mix | 0 | 0 |
Outback Lager | 10 | 15 | 66
Chef Anton's Gumbo Mix no tiene stock: su porcentaje sale vacío, que es lo honesto — no hay porcentaje de nada. Los demás se calculan con normalidad. Es el único caso en que fabricas un NULL a propósito: para que una fila imposible no tumbe las demás.
3.6 En el WHERE: IS DISTINCT FROM, un <> que no se rompe con NULL
Pregunta: ¿cuántos clientes no son de la región SP?
SELECT
count(*) AS no_son_de_sp
FROM
customers
WHERE
region <> 'SP'; -- 25
Hay 91 clientes y 6 son de SP, así que la respuesta debería ser 85. Da 25. Los 60 con region en NULL dan NULL <> 'SP' → UNKNOWN, y se van. Es el mismo problema de 3.3: para el motor, un cliente sin región no es de SP, pero tampoco es no de SP. No se sabe.
Con lo que ya tienes se arregla escribiéndolo a mano:
WHERE
region <> 'SP'
OR region IS NULL; -- 85
IS DISTINCT FROM es eso mismo en un solo operador:
SELECT
count(*) AS no_son_de_sp
FROM
customers
WHERE
region IS DISTINCT FROM 'SP'; -- 85
Se lee "es distinto de SP, contando el vacío como distinto". Trata NULL como un valor más: igual a sí mismo, distinto de todo lo demás. Por eso devuelve siempre TRUE o FALSE, nunca UNKNOWN, y ninguna fila se va sin evaluar. IS NOT DISTINCT FROM es el = equivalente: NULL IS NOT DISTINCT FROM NULL es cierto.
Cuándo usar cada uno. <> es el operador normal. IS DISTINCT FROM es para cuando se cumplen las dos cosas a la vez: la columna admite nulos y quieres que el nulo cuente como "distinto". Si la columna es NOT NULL, dan lo mismo y <> es lo que todo el mundo lee. Y si lo que quieres es excluir a los desconocidos —"clientes con región informada que no sea SP"—, entonces el 25 es la respuesta correcta y <> es el operador correcto; solo hay que decirlo (paso 2 de 3.3).
Dos avisos. No existe en SQL Server antes de 2022 ni en MySQL (ver la tabla de la sección 5). Y en PostgreSQL no usa el índice de la columna: recorre la tabla entera, mientras que <> y = sí lo aprovechan. En 91 clientes da igual; en millones de filas, no.
3.7 En el ORDER BY: dónde caen los nulos
En PostgreSQL, ORDER BY pone los NULL al final en orden ascendente y al principio en descendente. Se controla explícitamente:
SELECT
company_name,
region
FROM
customers
ORDER BY
region NULLS FIRST;
Más en ordenar-y-limitar.
4. Errores comunes
= NULLo<> NULL. Nunca es cierto. No da error. UsaIS NULL/IS NOT NULL.- Creer que
WHERE x <> 'A'devuelve "todo lo que no es A". Devuelve todo lo que se sabe que no es A. Los nulos se quedan fuera. NOT INcon una lista que puede traerNULL. Cero filas silenciosas, y la lista suele venir de otra consulta.- Concatenar sin
coalesce. UnNULLen medio anula la cadena entera. - Confundir
NULLcon cadena vacía.''es un texto de longitud cero: existe.'' IS NULLes falso. En Oracle no —es la excepción del mundo— pero en PostgreSQL y SQL Server son cosas distintas. - Interpretar todos los
NULLigual. "No aplica", "aún no" y "no lo sé" se escriben igual y significan cosas opuestas. Pregunta antes de rellenarlos.
5. Diferencias con SQL Server
| PostgreSQL | SQL Server | |
|---|---|---|
| Primer no nulo | coalesce(a,b) |
COALESCE(a,b) o ISNULL(a,b) |
| Concatenar saltando nulos | concat_ws(sep, a, b) |
CONCAT_WS(sep, a, b) desde SQL Server 2017 |
| Comparar con nulos | IS DISTINCT FROM |
No existe hasta SQL Server 2022; antes se emula con EXISTS(SELECT a INTERSECT SELECT b) |
| Orden de los nulos | Al final en ASC, configurable con NULLS FIRST/LAST |
Al principio en ASC, y no se puede cambiar directamente |
SET ANSI_NULLS OFF |
No existe | Existe (obsoleto): hace que = NULL funcione. No lo uses |
La tercera fila muerde: la misma consulta con ORDER BY da un orden distinto en cada motor. Si el orden importa, escríbelo.
6. Ejercicios
- Cuántos pedidos están sin enviar. Y cuántos clientes no tienen fax.
- Lista los clientes con su región, sustituyendo el nulo por el texto
n/a. - Corrige esta consulta, que pretende dar los productos que no son de la categoría 1:
sql SELECT product_name FROM products WHERE category_id <> 1;¿Cuándo daría un resultado incompleto y cuándo no? - Escribe una consulta que devuelva, por cada columna de
customers, cuántos nulos tiene. Hazlo pararegion,faxypostal_codeen una sola fila. - Sin ejecutarla, di qué devuelve:
sql SELECT count(*) FROM orders WHERE shipped_date IS NULL OR shipped_date IS NOT NULL;
7. Pregunta de negocio
Logística quiere el porcentaje de pedidos entregados con retraso. Te dan la definición: "retraso es cuando la fecha de envío es posterior a la fecha requerida".
Da el número. Y da también la frase que tienes que decir al entregarlo.