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

TrampaQué ocurreQué hacer
PRIMARY KEY no aplicadaEl planificador se la cree y devuelve resultados erróneosAñade siempre una regla duplicateCount
FOREIGN KEY no aplicadaHuérfanos tras backfills parcialesAñade una regla referentialIntegrity
Cola de WLM compartidaLas comprobaciones compiten con el ETLCola dedicada + regla de monitorización de consultas
Estadísticas obsoletasUna agregación se convierte en un broadcast joinValida después de ANALYZE
Longitudes de VARCHAR en bytesEl texto multibyte se trunca al cargarDimensiona con generosidad las columnas de texto
Sin ALTER DEFAULT PRIVILEGESLas tablas nuevas serán ilegibles mañanaConcede privilegios por defecto sobre el esquema
GETDATE() devuelve UTCLo contrario que SQL ServerMantén todo en UTC
Vistas de enlace tardíoLa comprobación falla si se elimina la tabla baseValida 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.