【Tutorial de SQL NULL】Domina la Extracción de Datos Conquistando IS NULL e IS NOT NULL
Cuando empiezas a aprender SQL, inevitablemente te encuentras con una entidad misteriosa: `NULL`. Se utiliza para representar un estado en el que no existen datos, como "un usuario con un número de teléfono no registrado" o "un producto sin nada escrito en el campo de notas". Sin embargo, el manejo de `NULL` es muy especial, y muchos principiantes se topan con un muro, preguntándose: "¿Por qué no puedo obtener los datos que esperaba?".
La principal razón de esto es intentar comparar `NULL` con = (igual), como si fuera lo mismo que "0" o una "cadena de texto vacía ('')". En el mundo de SQL, existe una regla filosófica: `NULL` no es igual a ningún otro valor, ni siquiera a sí mismo.
Este artículo explicará a fondo la naturaleza de `NULL` y los operadores dedicados para manejarlo correctamente, `IS NULL` e `IS NOT NULL`, utilizando código que puedes copiar y pegar. No es una exageración decir que quien domina `NULL`, domina SQL. ¡Aprovechemos esta oportunidad para dominar definitivamente la habilidad de comprobar la existencia de datos!
Preparación: Preparemos Datos de Miembros que Incluyan NULL
Para ver cómo se comporta `NULL` en la práctica, primero preparemos algunos datos de muestra. Esta vez, crearemos una tabla simple de members para gestionar la información de los miembros. La clave es registrar intencionadamente registros que contengan `NULL` (un estado donde los datos están vacíos), como en los números de teléfono y las fechas de último inicio de sesión.
-- Si la tabla members existe, la elimina (para pruebas repetibles)
DROP TABLE IF EXISTS members;
-- Crea una nueva tabla members
CREATE TABLE members (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
phone_number TEXT, -- Permite NULL
last_login DATE, -- Permite NULL
points INTEGER DEFAULT 0
);
-- Inserta datos iniciales
INSERT INTO members (id, name, email, phone_number, last_login, points) VALUES
(1, 'Yamada Taro', 'yamada@example.com', '090-1111-2222', '2025-06-25', 150),
(2, 'Suzuki Hanako', 'suzuki@example.com', NULL, '2025-07-01', 50),
(3, 'Sato Jiro', 'sato@example.com', '080-3333-4444', NULL, 200),
(4, 'Ito Sakura', 'ito@example.com', NULL, NULL, 0),
(5, 'Watanabe Kenta', 'watanabe@example.com', '', '2025-06-28', 300); -- El número de teléfono es una cadena vacía
Estos datos incluyen usuarios con un número de teléfono `NULL` (IDs 2, 4) y usuarios con un último inicio de sesión `NULL` (IDs 3, 4). Además, ten en cuenta que el número de teléfono de Watanabe (ID 5) no es `NULL` sino una **cadena de texto vacía** (''). Esta distinción será importante más adelante.
La Mayor Trampa: ¿Por Qué no se Puede Buscar con `= NULL`?
No es exagerado decir que el 100% de los principiantes de SQL caen en esta trampa: escribir WHERE phone_number = NULL. A primera vista, parece que debería funcionar correctamente, pero si ejecutas esta consulta, no se devolverá ni un solo registro.
-- 【¡ERROR!】Esta consulta no funcionará como se espera
SELECT * FROM members WHERE phone_number = NULL;
¿Por qué? Porque `NULL` representa un estado especial de "sin valor" o "desconocido". Como `NULL` no es un valor, cualquier comparación con él (incluso una comparación con `NULL` mismo) no resulta en `TRUE` ni en `FALSE`, sino en un tercer estado: `UNKNOWN` (desconocido). La cláusula WHERE solo extrae registros para los cuales la condición es `TRUE`, por lo que los registros que se evalúan como `UNKNOWN` nunca son seleccionados.
Imagina una encuesta con la pregunta "¿Comida favorita?". Si quieres encontrar a las personas que la dejaron "en blanco", no puedes encontrarlas buscando a quienes escribieron la palabra "en blanco". Necesitas buscar el estado de "estar en blanco" en sí mismo.
【Lo Básico】La Forma Correcta de Usar `IS NULL` e `IS NOT NULL`
SQL proporciona operadores dedicados para verificar este estado especial de `NULL`. Son `IS NULL` e `IS NOT NULL`.
`IS NULL`: Extrayendo Registros Donde un Valor es NULL
Si quieres encontrar registros donde el valor de una columna es `NULL` (los datos están vacíos), debes usar `IS NULL`, no `= NULL`.
Escenario: "Encontrar todos los miembros que no han registrado un número de teléfono (donde `phone_number` es `NULL`)."
SELECT * FROM members WHERE phone_number IS NULL;
Resultado:
id | name | email | phone_number | last_login | points
---|--------------|-------------------|--------------|------------|-------
2 | Suzuki Hanako| suzuki@example.com| NULL | 2025-07-01 | 50
4 | Ito Sakura | ito@example.com | NULL | NULL | 0
Esta vez, los usuarios con IDs 2 y 4 fueron extraídos correctamente como se esperaba. Ten en cuenta que Watanabe (ID 5) no está incluido en el resultado porque su número de teléfono es una "cadena vacía", no `NULL`.
`IS NOT NULL`: Extrayendo Registros Donde un Valor no es NULL
Por el contrario, si quieres encontrar registros donde hay algún dato (es decir, el valor no está vacío), usas `IS NOT NULL`.
Escenario: "Encontrar todos los miembros que tienen un registro del último inicio de sesión (donde `last_login` no es `NULL`)."
SELECT * FROM members WHERE last_login IS NOT NULL;
Resultado:
id | name | email | phone_number | last_login | points
---|----------------|----------------------|----------------|------------|-------
1 | Yamada Taro | yamada@example.com | 090-1111-2222 | 2025-06-25 | 150
2 | Suzuki Hanako | suzuki@example.com | NULL | 2025-07-01 | 50
5 | Watanabe Kenta | watanabe@example.com | | 2025-06-28 | 300
Como puedes ver, usar `IS NOT NULL` te permite seleccionar eficientemente solo los registros que no tienen datos faltantes.
Aplicación: Entendiendo la Diferencia Entre `NULL`, Cadenas Vacías y `0`
Los principiantes a menudo tratan las "cadenas vacías ('')" y el "número 0" como si fueran lo mismo que `NULL`. En SQL, estos se tratan como cosas claramente diferentes.
- `NULL`: Un estado de ser "desconocido" o "inexistente".
- Cadena Vacía (
''): Un "valor" que es una cadena de longitud cero. 0: Un "valor" numérico de cero.
El número de teléfono de Watanabe (ID 5) es una cadena vacía, no `NULL`. Por lo tanto, la siguiente consulta solo encontrará al usuario con ID 5.
SELECT * FROM members WHERE phone_number = '';
Del mismo modo, los puntos de Ito (ID 4) son `0`, pero esto es diferente de `NULL`. Esta diferencia se vuelve aún más clara al usar funciones de agregación como `COUNT`.
-- Miembros totales, miembros con un número de teléfono registrado, y suma de puntos
SELECT COUNT(*), COUNT(phone_number), SUM(points) FROM members;
Resultado:
COUNT(*) | COUNT(phone_number) | SUM(points)
--------|---------------------|------------
5 | 3 | 650
COUNT(*) cuenta todos los registros, por lo que es 5, pero COUNT(phone_number) ignora los valores `NULL`, por lo que es 3 (IDs 1, 3 y 5). Del mismo modo, SUM(points) también excluye los registros con `NULL` de su cálculo. Este comportamiento es muy importante en el análisis de datos, así que asegúrate de recordarlo.
【Patio de Recreo Interactivo】¡Comprueba el Comportamiento de NULL Ejecutando SQL en tu Navegador!
¡Ahora es el momento de convertir el conocimiento en una habilidad! Copia todo el código HTML a continuación, guárdalo como un archivo llamado sql_null_test.html y ábrelo en tu navegador. Se iniciará tu propio entorno SQL personal, precargado con la tabla members que hemos estado usando en este artículo.
¡Comprueba por ti mismo cómo difieren los resultados entre = NULL e IS NULL, y por qué COUNT(*) y COUNT(column_name) dan resultados diferentes. ¡Vamos a descubrirlo ensuciándonos las manos!
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Patio de Recreo en Línea de SQL NULL</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; line-height: 1.7; color: #333; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
h1 { color: #34495e; }
textarea { width: 100%; height: 180px; font-family: "SF Mono", "Consolas", monospace; font-size: 16px; padding: 12px; border: 1px solid #ccc; border-radius: 6px; box-sizing: border-box; margin-bottom: 1rem; }
button { background-color: #34495e; color: white; border: none; padding: 12px 22px; font-size: 16px; border-radius: 6px; cursor: pointer; transition: background-color 0.2s; }
button:hover { background-color: #2c3e50; }
button:disabled { background-color: #bdc3c7; cursor: not-allowed; }
#result-container { margin-top: 2rem; border: 1px solid #ddd; padding: 1rem; border-radius: 6px; background: #fdfdfd; min-height: 50px; }
#error-message { color: #e74c3c; font-weight: bold; }
table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
th { background-color: #f2f2f2; }
tr:nth-child(even) { background-color: #f9f9f9; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/sql-wasm.js"></script>
</head>
<body>
<h1>¡Probemos SQL!</h1>
<p>Introduce una sentencia SQL en el área de texto de abajo y haz clic en el botón "Ejecutar". ¡Prueba con diferentes condiciones!</p>
<textarea id="sql-input">-- Encontrar miembros que nunca han iniciado sesión (last_login es NULL)
SELECT * FROM members WHERE last_login IS NULL;
</textarea>
<button id="execute-btn">Ejecutar</button>
<div id="result-container">
<p id="error-message"></p>
<div id="result-output"></div>
</div>
<script>
const sqlInput = document.getElementById('sql-input');
const executeBtn = document.getElementById('execute-btn');
const errorMsg = document.getElementById('error-message');
const resultOutput = document.getElementById('result-output');
let db;
async function initDb() {
executeBtn.disabled = true;
executeBtn.textContent = 'Inicializando BD...';
try {
const SQL = await initSqlJs({
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/${file}`
});
db = new SQL.Database();
const setupSql = `
DROP TABLE IF EXISTS members;
CREATE TABLE members (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, phone_number TEXT, last_login DATE, points INTEGER DEFAULT 0);
INSERT INTO members (id, name, email, phone_number, last_login, points) VALUES
(1, 'Yamada Taro', 'yamada@example.com', '090-1111-2222', '2025-06-25', 150),
(2, 'Suzuki Hanako', 'suzuki@example.com', NULL, '2025-07-01', 50),
(3, 'Sato Jiro', 'sato@example.com', '080-3333-4444', NULL, 200),
(4, 'Ito Sakura', 'ito@example.com', NULL, NULL, 0),
(5, 'Watanabe Kenta', 'watanabe@example.com', '', '2025-06-28', 300);
`;
db.run(setupSql);
executeBtn.disabled = false;
executeBtn.textContent = 'Ejecutar';
resultOutput.innerHTML = '<p>¡Todo listo! Siéntete libre de probar tus propias consultas SQL.</p>';
} catch (err) {
errorMsg.textContent = 'Error al inicializar la base de datos: ' + err.message;
console.error(err);
}
}
function executeSql() {
if (!db) return;
const sql = sqlInput.value;
errorMsg.textContent = '';
resultOutput.innerHTML = '';
try {
const results = db.exec(sql);
if (results.length === 0) {
resultOutput.innerHTML = '<p>Consulta ejecutada con éxito, pero no se devolvió ningún conjunto de resultados.</p>';
return;
}
results.forEach(result => {
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
const headerRow = document.createElement('tr');
result.columns.forEach(colName => {
const th = document.createElement('th');
th.textContent = colName;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
result.values.forEach(row => {
const bodyRow = document.createElement('tr');
row.forEach(cellValue => {
const td = document.createElement('td');
td.textContent = cellValue === null ? 'NULL' : cellValue;
bodyRow.appendChild(td);
});
tbody.appendChild(bodyRow);
});
table.appendChild(thead);
table.appendChild(tbody);
resultOutput.appendChild(table);
});
} catch (err) {
errorMsg.textContent = 'Error de SQL: ' + err.message;
console.error(err);
}
}
executeBtn.addEventListener('click', executeSql);
initDb();
</script>
</body>
</html>
Conclusión
En este artículo, hemos aprendido sobre la forma correcta de manejar `NULL` en SQL, cubriendo su concepto y métodos específicos de verificación.
- La Naturaleza de `NULL`: `NULL` es un estado especial que representa "sin valor" o "desconocido" y es completamente diferente de "0" o una "cadena vacía".
- La Regla de Comparación: Comparar con `NULL` (
=,<>) siempre resulta en `UNKNOWN`, por lo que no producirá los resultados esperados en una cláusulaWHERE. - El Método de Verificación Correcto: La única forma correcta de verificar si algo es `NULL` es usar
IS NULL, y para verificar si no es `NULL`, usarIS NOT NULL. - Relación con Funciones de Agregación: Funciones de agregación como
COUNT(nombre_columna),SUM(), yAVG()excluyen automáticamente los registros con `NULL` de sus cálculos.
Entender correctamente el concepto de `NULL` es un paso ineludible para dominar SQL. Puede ser un poco confuso al principio, pero si tienes en cuenta que "`NULL` no es un valor, sino un estado", te acostumbrarás gradualmente. Esta habilidad es esencial para manejar correctamente los datos faltantes y desarrollar aplicaciones robustas y sin errores. ¡Tu nivel de SQL acaba de subir otro peldaño!