Error SQL · operaciones de conjuntos y tipos

UNION con tipos incompatibles: convierte cada posición a un tipo común

Si las ramas de UNION tienen la misma cantidad de columnas pero una posición mezcla tipos que el motor no puede reconciliar, el problema ya no es la forma de la fila: es el contrato de tipos de esa posición. Define qué significa el dato y convierte de forma explícita solo cuando esa conversión conserva la intención.

Lectura: 15–20 minComparativa por motorPráctica con pistas

El síntoma: cada rama devuelve las mismas columnas, pero una posición no comparte un tipo utilizable

En UNION, UNION ALL, INTERSECT y EXCEPT, las columnas se emparejan por posición. Tener el mismo número de columnas es solo la primera comprobación: cada posición debe poder convertirse a un tipo común según las reglas del motor.

PostgreSQL resuelve un tipo común para cada columna y falla cuando los tipos no pertenecen a una categoría compatible o no existe una conversión implícita válida. SQL Server también exige tipos compatibles y aplica su prioridad de tipos para decidir qué conversión intentar. MySQL es más permisivo en varios casos y puede construir un tipo de resultado a partir de valores de tipos distintos. SQLite usa tipado dinámico, por lo que puede aceptar combinaciones que otros motores rechazan. Que una unión funcione en un motor no demuestra que sea portable.

Ejemplo: dos columnas a cada lado, pero tipos incompatibles en PostgreSQL

La forma de ambas ramas es idéntica: orden y valor. El conflicto está en la segunda posición, donde una rama produce un entero y la otra texto.

PostgreSQL · consulta problemática
SELECT 1 AS orden,
       42::integer AS valor
UNION ALL
SELECT 2,
       'sin-dato'::text;

Con las reglas de resolución de tipos de PostgreSQL, integer y text no forman una categoría común para esa columna y la unión se rechaza. En SQL Server una combinación equivalente puede intentar convertir el texto al tipo de mayor prioridad y fallar si el valor no es convertible. MySQL, en cambio, documenta que SELECT 1 UNION SELECT 'a' es válido y construye un resultado capaz de contener ambos valores.

El mismo SQL puede comportarse distinto según el motor

MotorQué revisarConsecuencia práctica
PostgreSQLResuelve un tipo común por columna; si las categorías no son compatibles o no existe conversión implícita, falla.Conviene declarar el tipo objetivo con CAST cuando la semántica es realmente común.
SQL ServerLas columnas deben ser compatibles mediante conversión implícita y se aplica prioridad de tipos.Un texto no convertible puede provocar un error al intentar llevarlo a un tipo con mayor prioridad.
MySQLLos tipos correspondientes deberían coincidir, pero el tipo final puede ampliarse o adaptarse considerando todas las ramas.Una consulta aceptada puede no ser portable a motores con resolución más estricta.
SQLiteEl tipo pertenece al valor y el motor usa tipado dinámico.Puede conservar valores de clases distintas en la misma columna de una consulta compuesta.

Comprobación local: SQLite acepta valores heterogéneos

La siguiente consulta se ejecutó localmente con SQLite durante esta revisión. El resultado muestra que la misma columna contiene un entero en una fila y texto en otra.

SQLite · comportamiento permitido
WITH combinados AS (
  SELECT 1 AS orden, 42 AS valor
  UNION ALL
  SELECT 2, 'sin-dato'
)
SELECT orden,
       valor,
       typeof(valor) AS tipo
FROM combinados
ORDER BY orden;
Resultado local en SQLite
ordenvalortipo
142integer
2sin-datotext

Este resultado no contradice a PostgreSQL o SQL Server: SQLite tiene un sistema de tipos diferente. Si tu consulta debe viajar entre motores, define el tipo de salida de forma deliberada en vez de depender de coerciones implícitas.

Corrección: decide el significado de la columna y convierte hacia ese contrato

Antes de añadir CAST, pregunta qué representa la posición. Si valor es una etiqueta que a veces contiene números y a veces texto, convertir ambas ramas a texto puede ser correcto. Si representa un importe, convertir 'sin-dato' a texto solo oculta un problema de calidad: deberías limpiar el dato o representarlo como NULL.

Contrato textual explícito
SELECT 1 AS orden,
       CAST(42 AS VARCHAR(20)) AS valor
UNION ALL
SELECT 2,
       CAST('sin-dato' AS VARCHAR(20));

La conversión explícita documenta la intención y reduce diferencias entre motores. Aun así, el tipo elegido debe representar el dominio real del dato; no uses texto como salida universal solo para silenciar errores.

Método de diagnóstico

  1. Cuenta las columnas de cada rama. Si no coinciden, corrige primero la forma de la fila.
  2. Compara los tipos posición por posición. No te fijes solo en los nombres de alias.
  3. Define qué significa cada posición. ¿Es un importe, una fecha, un identificador o una etiqueta?
  4. Revisa las reglas del motor. Una coerción implícita aceptada por MySQL o SQLite puede fallar en PostgreSQL o SQL Server.
  5. Usa CAST solo hacia un tipo semánticamente correcto. Si el origen contiene valores inválidos, corrige los datos o trátalos de forma explícita.

Error frecuente: convertir todo a texto para que UNION deje de fallar

Un CAST puede esconder una incompatibilidad de significado

Si una rama contiene importes y la otra estados como 'pendiente', ambas pueden convertirse a texto, pero eso no convierte la columna resultante en un dato coherente. El objetivo no es que el motor acepte la consulta: es que cada posición represente el mismo concepto.

Práctica correctiva

Intermedia · conjuntos y tipos

Combina precios actuales con precios importados como texto

La fuente actual entrega precio como número. La fuente legacy entrega precio_texto, pero en este ejercicio todos sus valores contienen números válidos. Devuelve codigo y precio como valor numérico en una sola lista.

Qué debes recordar

  • El mismo número de columnas no garantiza tipos compatibles.
  • Las columnas se emparejan por posición, no por nombre.
  • PostgreSQL y SQL Server pueden rechazar o fallar en conversiones que MySQL o SQLite aceptan de otra forma.
  • CAST debe expresar un tipo común real, no ocultar datos incoherentes.

Conceptos relacionados

Fuentes técnicas

La resolución de tipos y las diferencias entre motores de esta guía se contrastaron con documentación primaria: