El síntoma: usas una subconsulta como si fuera un único valor
Una expresión como precio = (SELECT ...) obliga a preguntarte cuántas filas puede producir la consulta interior. En PostgreSQL, una subconsulta escalar debe devolver exactamente una fila y una columna; si devuelve varias filas, el motor rechaza la expresión. SQL Server expone el mismo problema con el error 512 cuando una subconsulta de ese tipo produce más de un valor.
El diagnóstico importante no es “cómo silenciar el mensaje”, sino qué cardinalidad necesita realmente la consulta exterior. Si esperabas una lista, la solución suele ser expresar una operación de conjunto. Si esperabas un único valor, la consulta interior debe demostrar por qué solo puede devolver uno.
Ejemplo reproducible: dos ciudades donde la consulta esperaba una
WITH clientes(id, ciudad) AS (
VALUES (1, 'Madrid'),
(2, 'Sevilla'),
(3, 'Madrid')
)
SELECT id, ciudad
FROM clientes
WHERE ciudad = (
SELECT ciudad
FROM clientes
WHERE id IN (1, 2)
);La subconsulta interior produce dos filas: Madrid y Sevilla. El operador = necesita comparar con un solo valor. En PostgreSQL y SQL Server esa forma provoca un error de cardinalidad.
Corrección 1: usa IN cuando la intención es “cualquiera de estos valores”
WITH clientes(id, ciudad) AS (
VALUES (1, 'Madrid'),
(2, 'Sevilla'),
(3, 'Madrid')
)
SELECT id, ciudad
FROM clientes
WHERE ciudad IN (
SELECT ciudad
FROM clientes
WHERE id IN (1, 2)
)
ORDER BY id;| id | ciudad |
|---|---|
| 1 | Madrid |
| 2 | Sevilla |
| 3 | Madrid |
Corrección 2: demuestra la unicidad si de verdad necesitas un valor
Si la regla de negocio dice que la consulta interior identifica una sola fila, exprésala mediante una clave o condición que garantice esa unicidad. Por ejemplo, WHERE id = 1 puede producir un único registro cuando id es una clave única.
No uses LIMIT 1 como parche automático. Elegir una fila arbitraria oculta que la consulta original no definía cuál de las varias filas era la correcta. Solo limita a una fila cuando exista un criterio explícito y, si el orden importa, un ORDER BY que lo haga determinista.
Diferencia importante: SQLite no diagnostica este caso igual
SQLite define el valor de una subconsulta escalar como la primera fila devuelta y usa NULL cuando no hay filas. Eso significa que una consulta que falla en PostgreSQL o SQL Server puede ejecutarse en SQLite y seleccionar silenciosamente una fila. Por portabilidad, no dependas de ese comportamiento: documenta si esperas exactamente una fila o un conjunto.
Método de diagnóstico
- Ejecuta la subconsulta sola y cuenta sus filas.
- Identifica el contexto exterior:
=y una expresión escalar esperan un valor;INyEXISTSmodelan conjuntos o existencia. - Comprueba la unicidad: una clave única es evidencia; “normalmente sale una fila” no lo es.
- Predice el caso de cero filas y decide si debe producir
NULL, falso, ninguna coincidencia u otro comportamiento.
Error frecuente: cambiar = por IN sin revisar la intención
El error desaparece, pero la pregunta puede cambiar
IN es correcto cuando quieres comparar contra un conjunto. Si la aplicación esperaba una única configuración, tarifa o responsable, varias filas pueden indicar un problema de datos o una condición incompleta. Corrige la cardinalidad, no solo la sintaxis.
Práctica correctiva
Elige entre valor único e IN
Quieres listar clientes que viven en cualquiera de las ciudades de los clientes 1 y 2. ¿Qué operador usarías y por qué?
Solución razonada
Usa IN. La consulta exterior no necesita reducir el conjunto a un único valor: necesita comprobar pertenencia.
SELECT id, ciudad
FROM clientes
WHERE ciudad IN (
SELECT ciudad
FROM clientes
WHERE id IN (1, 2)
);Qué debes recordar
- Una subconsulta escalar representa un valor, no una lista.
- Ejecuta primero la consulta interior y comprueba su cardinalidad.
- Usa
INoEXISTScuando la intención sea trabajar con un conjunto. - No uses
LIMIT 1para esconder una condición que debería ser única. - SQLite y otros motores pueden tratar este caso de forma diferente, así que explicita la intención.