Cómo validar datos en Amazon Redshift
Guía práctica para validar la calidad de datos en Amazon Redshift — las seis comprobaciones que necesita toda tabla, cómo mantenerlas fuera de la cola de WLM, la trampa de las restricciones no aplicadas y cómo ejecutarlas como un contrato de datos versionado.
· 9 min read
Para validar datos en Amazon Redshift, expresa cada expectativa como una consulta de agregación que devuelva un recuento de violaciones y ejecuta el lote después de cada carga. Las seis comprobaciones que merece la pena tener son: nulos en columnas obligatorias, claves duplicadas, valores fuera de un conjunto permitido, números fuera de un rango plausible, cadenas mal formadas y filas obsoletas. La herencia de Postgres de Redshift hace que la mayor parte del SQL resulte familiar, pero dos cosas no lo son: sus restricciones se declaran y nunca se aplican, así que el optimizador puede producir respuestas incorrectas a partir de una clave duplicada, y las consultas de validación compiten por slots en una cola de WLM que también usa tu ETL.
Por qué Redshift exige cuidado
Las restricciones son pistas, no reglas. Redshift acepta declaraciones PRIMARY KEY, UNIQUE y FOREIGN KEY, y no aplica ninguna de ellas. Peor aún: el planificador se las cree. Si declaras order_id como único y no lo es, una consulta que dependa de esa suposición puede devolver resultados incorrectos, no simplemente lentos. En Redshift, validar la unicidad no es higiene defensiva: es proteger la corrección de todas las consultas aguas abajo.
Todo comparte una cola. Redshift ejecuta las consultas a través de la gestión de cargas de trabajo (WLM). Un barrido de validación con escaneos de tablas completas, lanzado a la vez que el ETL nocturno, se pondrá en cola detrás de él o, peor, le quitará slots. Dale a la validación su propia cola de WLM con una concurrencia modesta y una regla de monitorización de consultas que aborte todo lo que supere un umbral de duración.
Las estadísticas obsoletas cambian el significado de «barato». Tras un COPY grande, las estadísticas de la tabla están desactualizadas hasta que se ejecuta ANALYZE. Los planes elegidos con estadísticas obsoletas pueden convertir una agregación rápida en un broadcast join. Ejecuta la validación después de ANALYZE, no antes.
Más allá de esas tres cosas, validar en Redshift tiene la misma forma que en cualquier otro sitio, que es lo que expone de principio a fin la guía completa para validar datos.
Las seis comprobaciones que necesita toda tabla
1. Completitud — nulos en columnas obligatorias
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE order_id IS NULL;
Redshift es columnar, así que esto lee solo la columna order_id: barato incluso en una tabla de hechos muy ancha. No escribas nunca SELECT * en una comprobación; renuncias justo a esa ventaja.
2. Unicidad — claves de negocio duplicadas
SELECT COUNT(*) AS violations
FROM (
SELECT order_id
FROM sales.orders
WHERE order_id IS NOT NULL
GROUP BY order_id
HAVING COUNT(*) > 1
) dupes;
Esta es la comprobación que hay que ejecutar primero en cualquier tabla de Redshift. Los duplicados aparecen de forma rutinaria: un COPY reintentado tras un fallo parcial, un MERGE implementado como borrar-y-luego-insertar donde el filtro del borrado se quedó corto, una extracción aguas arriba que se vuelve a ejecutar para la misma ventana. Como la clave primaria declarada no hace nada, no hay nada más que vaya a detectarlos.
Si la tabla tiene una sort key sobre la clave de negocio, el GROUP BY se ejecuta contra bloques ordenados y sale sustancialmente más barato. Vale la pena elegir las sort keys pensando en eso.
3. Conformidad — valores fuera de un conjunto permitido
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE status IS NOT NULL
AND status NOT IN ('pending', 'paid', 'shipped', 'refunded');
Mantén la protección frente a nulos: NULL NOT IN (...) es NULL, la fila desaparece y una columna con muchos nulos sale impoluta.
Las longitudes de VARCHAR en Redshift se miden en bytes, no en caracteres. Un emoji de cuatro bytes en una columna VARCHAR(10) se trunca durante la carga, y un valor truncado falla la comprobación de conjunto permitido por un motivo que no tiene nada que ver con el sistema de origen. Dimensiona con generosidad las columnas de texto.
4. Exactitud — números fuera de un rango plausible
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE total_amount IS NOT NULL
AND (total_amount < 0 OR total_amount > 100000);
Usa DECIMAL/NUMERIC para el dinero. La aritmética de DECIMAL en Redshift también puede desbordarse en silencio hacia una escala más amplia durante una agregación, así que una comprobación de rango sobre la columna en bruto es más fiable que una sobre un total calculado.
5. Conformidad — identificadores mal formados
Redshift conserva los operadores de expresiones regulares de Postgres:
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE reference IS NOT NULL
AND reference !~ '^ORD-[0-9]{6}#39;;
~ distingue mayúsculas de minúsculas, ~* no las distingue y !~ niega. También están disponibles SIMILAR TO y REGEXP_COUNT/REGEXP_SUBSTR. No hace falta traducir patrones: a diferencia de SQL Server, el patrón que escribes es el patrón que se ejecuta.
6. Oportunidad — frescura
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE created_at < GETDATE() - INTERVAL '24 hours';
GETDATE() en Redshift devuelve UTC, que es justo lo contrario del comportamiento de SQL Server y una fuente genuina de confusión al portar reglas entre ambos. SYSDATE también devuelve UTC. Guarda las marcas de tiempo como TIMESTAMP (Redshift no almacena zona horaria) y mantén todo en UTC por convención.
Para una comprobación de finalización de carga que no toque los datos de la tabla, SVV_TABLE_INFO ofrece metadatos por tabla a bajo coste.
Integridad referencial
SELECT COUNT(*) AS violations
FROM sales.orders o
LEFT JOIN sales.customers c ON o.customer_id = c.id
WHERE o.customer_id IS NOT NULL
AND c.id IS NULL;
Como las claves foráneas no se aplican, las filas huérfanas son habituales tras un backfill parcial. El estilo de distribución importa para el coste de esta comprobación: si ambas tablas tienen DISTKEY sobre la columna del join, el join es local a cada slice; si no, Redshift redistribuye uno de los lados por todo el clúster. En tablas grandes, esa es la diferencia entre segundos y minutos.
Un usuario de solo lectura seguro
CREATE USER catalyst_ro PASSWORD '<generated>';
GRANT USAGE ON SCHEMA sales TO catalyst_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO catalyst_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA sales
GRANT SELECT ON TABLES TO catalyst_ro;
La línea de privilegios por defecto importa aquí tanto como en Postgres: si no, las tablas que recree el ETL de mañana son invisibles para el usuario de validación, y las comprobaciones empiezan a fallar con errores de permisos que parecen fallos de datos.
Después, aísla la carga de trabajo:
-- In the WLM configuration, give the validation user group its own queue with
-- low concurrency, and a query monitoring rule that aborts on long runtime.
CREATE GROUP validators WITH USER catalyst_ro;
De SQL ad hoc a un contrato de datos
Declarar las expectativas las hace revisables, portables e independientes de la herramienta que cargue la tabla. Catalyst usa el Open Data Contract Standard:
apiVersion: v3.0.0
kind: DataContract
info:
title: orders
version: 1.3.0
owner: data-platform
schema:
- name: orders
physicalName: orders
physicalType: table
properties:
- name: order_id
logicalType: string
physicalType: varchar(36)
required: true
primaryKey: true
quality:
- rule: nullCount
dimension: completeness
severity: error
mustBe: "0"
- rule: duplicateCount
dimension: uniqueness
severity: error
mustBe: "0"
- name: customer_id
logicalType: string
quality:
- rule: referentialIntegrity
dimension: consistency
severity: error
mustBe: "customers.id"
- name: status
logicalType: string
quality:
- rule: validValues
dimension: conformity
severity: error
mustBe: "['pending', 'paid', 'shipped', 'refunded']"
- name: total_amount
logicalType: number
physicalType: numeric(12,2)
quality:
- rule: between
dimension: accuracy
severity: error
mustBe: "[0, 100000]"
- name: reference
logicalType: string
quality:
- rule: regex
dimension: conformity
severity: warning
mustBe: "'^ORD-[0-9]{6}#39;"
- name: created_at
logicalType: timestamp
quality:
- rule: freshness
dimension: timeliness
severity: error
mustBe: "<= 24h"
quality:
- rule: rowCount
dimension: consistency
severity: warning
mustBe: "> 0"
Catalyst importa las columnas desde information_schema, propone un contrato de base, compila cada regla a SQL de Redshift y registra pass, warn o fail por comprobación junto con una muestra de las filas que fallan. El YAML va y vuelve a través del constructor visual —con los comentarios y el orden intactos—, así que el fichero que se revisa en una pull request es el que se ejecuta.
Trampas de Redshift que conviene conocer
| Trampa | Qué ocurre | Qué hacer |
|---|---|---|
PRIMARY KEY no aplicada | El planificador se la cree y devuelve resultados erróneos | Añade siempre una regla duplicateCount |
FOREIGN KEY no aplicada | Huérfanos tras backfills parciales | Añade una regla referentialIntegrity |
| Cola de WLM compartida | Las comprobaciones compiten con el ETL | Cola dedicada + regla de monitorización de consultas |
| Estadísticas obsoletas | Una agregación se convierte en un broadcast join | Valida después de ANALYZE |
Longitudes de VARCHAR en bytes | El texto multibyte se trunca al cargar | Dimensiona con generosidad las columnas de texto |
Sin ALTER DEFAULT PRIVILEGES | Las tablas nuevas serán ilegibles mañana | Concede privilegios por defecto sobre el esquema |
GETDATE() devuelve UTC | Lo contrario que SQL Server | Mantén todo en UTC |
| Vistas de enlace tardío | La comprobación falla si se elimina la tabla base | Valida tablas base, no vistas |
Programación y alertas
Dispara la validación desde la finalización del COPY o del job de transformación, después de ANALYZE, en lugar de desde un reloj fijo. En Redshift Serverless vale el mismo consejo con un incentivo extra: la capacidad ociosa no cuesta nada, pero un barrido de validación que despierta el workgroup cada quince minutos lo mantiene caliente y facturando.
Alerta sobre transiciones en lugar de sobre el estado continuado, y separa las severidades para que error quede reservado a las comprobaciones que de verdad deberían bloquear a los consumidores aguas abajo. En Redshift, la regla de unicidad casi siempre corresponde al nivel error, dado lo que una clave primaria no aplicada le hace a la corrección de las consultas.
Preguntas frecuentes
¿Redshift aplica las claves primarias?
No. PRIMARY KEY, UNIQUE y FOREIGN KEY se aceptan y se registran, pero nunca se aplican en escritura. El planificador de consultas sí se las cree, lo que significa que una clave duplicada puede hacer que una consulta devuelva resultados incorrectos, no solo filas de más. Validar la unicidad con una comprobación explícita es la única forma de aplicación disponible.
¿Puedo validar datos de Redshift sin escribir SQL?
Sí. Declara la expectativa —obligatorio, único, valores permitidos, rango numérico, expresión regular, antigüedad máxima, clave foránea— y deja que la herramienta la compile a SQL de Redshift. Catalyst importa el esquema desde information_schema, sugiere un conjunto de reglas de base y reserva el SQL escrito a mano para la lógica genuinamente a medida, mediante una regla customSql.
¿Cómo evito que las consultas de validación ralenticen mi ETL?
Pon al usuario de validación en su propia cola de WLM, con baja concurrencia y una regla de monitorización de consultas que aborte las de larga duración, y programa las comprobaciones para que sigan a la carga en vez de ejecutarse en paralelo. Mantén las comprobaciones acotadas a columnas: Redshift es columnar, así que una agregación sobre una sola columna sale radicalmente más barata que cualquier cosa que toque la fila entera.
¿Funciona esto con Redshift Serverless y Redshift Spectrum?
Redshift Serverless se comporta de forma idéntica para todo lo de esta guía; la única diferencia es la facturación, así que evita programaciones que mantengan el workgroup despierto sin necesidad. Las tablas externas de Spectrum se validan sin problema, pero no hay almacenamiento local ni sort key que aprovechar, así que cada comprobación escanea los objetos subyacentes en S3: acótalas a una partición.
¿Qué permisos necesita una herramienta de validación?
USAGE sobre el esquema y SELECT sobre las tablas, más los privilegios por defecto para que las tablas futuras hereden la concesión. No hace falta acceso de escritura. Catalyst se conecta en modo solo lectura y guarda únicamente metadatos y resultados de las comprobaciones.