Cómo validar datos en SQL Server
Guía práctica para validar la calidad de datos en Microsoft SQL Server — las seis comprobaciones que necesita toda tabla, el T-SQL para escribirlas y cómo convertirlas en un contrato de datos versionado que se ejecuta de forma programada.
· 9 min read
La forma más rápida de validar datos en SQL Server es expresar cada expectativa como una única consulta de agregación que devuelve un recuento de incumplimientos, ejecutarlas todas contra la tabla en una sola pasada y hacer que el lote falle cuando un recuento supere su umbral. Seis comprobaciones cubren la mayoría de las roturas reales: 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. Esta guía muestra el T-SQL de cada una, las rarezas de SQL Server que te van a morder y cómo pasar de scripts improvisados a un contrato que se ejecuta de forma programada.
Por qué SQL Server necesita su propio enfoque
La mayoría de los consejos sobre calidad de datos están escritos para Postgres y dan por supuestas, sin decirlo, cosas que T-SQL no tiene. Importan tres diferencias:
- No hay operador de expresiones regulares.
LIKEes el único mecanismo de coincidencia de patrones disponible de serie. Admite clases de caracteres[0-9]y los comodines%y_, lo que basta para patrones anclados y de forma fija, y nada más. - La limitación de filas es
TOP n, noLIMIT n, y va antes de la lista de selección en lugar de al final de la sentencia. Cualquier herramienta que construya consultas de muestreo tiene que saberlo. - La collation decide la igualdad. Una columna con la collation por defecto
SQL_Latin1_General_CP1_CI_AStrata'ACTIVE'y'active'como el mismo valor. Una collation sensible a mayúsculas no. Tu comprobación de “valores permitidos” significa en silencio cosas distintas en dos servidores.
Todo lo que sigue está escrito teniendo eso en cuenta. Para las partes que son iguales en todos los motores —dónde ejecutar las comprobaciones, cómo programarlas, qué tablas priorizar— empieza por la guía completa para validar datos.
Las seis comprobaciones que necesita toda tabla
1. Completitud — nulos en columnas obligatorias
SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [order_id] IS NULL;
Usa COUNT_BIG en lugar de COUNT en tablas de hechos grandes: COUNT devuelve un int y desborda por encima de los 2100 millones de filas. Fíjate también en que COUNT(column) ya se salta los nulos, y por eso la comprobación se escribe como un COUNT(*) filtrado: las dos son fáciles de confundir y la equivocada siempre devuelve cero incumplimientos.
2. Unicidad — claves de negocio duplicadas
SELECT COUNT_BIG(*) AS violations
FROM (
SELECT [order_id]
FROM [sales].[orders]
WHERE [order_id] IS NOT NULL
GROUP BY [order_id]
HAVING COUNT(*) > 1
) AS dupes;
Una restricción de clave primaria lo impediría, pero las tablas analíticas normalmente las carga un job de ELT en un heap sin ninguna restricción. La comprobación de duplicados es lo que te cuesta esa restricción que falta.
3. Conformidad — valores fuera de un conjunto permitido
SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [status] IS NOT NULL
AND [status] NOT IN ('pending', 'paid', 'shipped', 'refunded');
Deja puesta la protección IS NOT NULL. Un NOT IN con un nulo a la izquierda se evalúa como UNKNOWN, la fila se descarta, y una columna con un 40 % de nulos parece perfectamente conforme.
4. Exactitud — números fuera de un rango plausible
SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [total_amount] IS NOT NULL
AND ([total_amount] < 0 OR [total_amount] > 100000);
Las comprobaciones de rango detectan errores de unidad —céntimos cargados como euros, un decimal desplazado por una conversión de moneda— que ninguna restricción de esquema llegará a ver nunca.
5. Conformidad — identificadores mal formados
Aquí es donde T-SQL se separa del resto. No existe el operador ~, así que un patrón anclado hay que expresarlo con LIKE:
-- ^ORD-[0-9]{6}$ becomes:
SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [reference] IS NOT NULL
AND [reference] NOT LIKE 'ORD-[0-9][0-9][0-9][0-9][0-9][0-9]';
LIKE queda implícitamente anclado por ambos extremos cuando el patrón no lleva % al principio ni al final, así que esto es un equivalente exacto de la regex anclada. Lo que no puedes expresar es alternancia, grupos opcionales, retrorreferencias ni repetición no acotada. Para eso, recurre a una lista explícita de valores permitidos o a una comprobación customSql escrita a mano, en lugar de fingir que LIKE es un motor de regex.
Catalyst hace esa traducción por ti: una regla regex sobre una conexión de SQL Server se convierte al patrón LIKE equivalente cuando es expresable, y lanza un error explícito de “no soportado en este dialecto” cuando no lo es, de modo que la comprobación falla a las claras en vez de pasar en silencio.
6. Actualidad — frescura
SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [created_at] < DATEADD(HOUR, -24, SYSUTCDATETIME());
Usa SYSUTCDATETIME(), no GETDATE(). GETDATE() devuelve la hora local del servidor, así que la misma comprobación se desvía una hora dos veces al año y horas enteras entre regiones. Guarda las marcas de tiempo como datetime2 y compara en UTC.
Cómo crear un login de solo lectura seguro
La validación nunca debería necesitar acceso de escritura. Un login mínimo tiene este aspecto:
CREATE LOGIN catalyst_ro WITH PASSWORD = '<generated>';
CREATE USER catalyst_ro FOR LOGIN catalyst_ro;
ALTER ROLE db_datareader ADD MEMBER catalyst_ro;
GRANT VIEW DEFINITION TO catalyst_ro; -- needed to read INFORMATION_SCHEMA
db_datareader concede SELECT sobre todas las tablas; VIEW DEFINITION es lo que permite a la cuenta enumerar columnas y tipos desde INFORMATION_SCHEMA.COLUMNS. Apúntalo a una secundaria legible si tienes un grupo de disponibilidad: las consultas de validación son escaneos agregados y su sitio está fuera de la primaria.
De SQL improvisado a un contrato de datos
Los scripts como los anteriores se pudren. Viven en el repositorio de alguien, nadie sabe cuáles siguen ejecutándose y los umbrales son invisibles para los analistas que dependen de ellos. La solución es expresar las expectativas de forma declarativa, en un formato que puedan leer tanto las personas como las herramientas.
Catalyst usa para esto el Open Data Contract Standard (ODCS). Las seis comprobaciones anteriores se convierten en:
apiVersion: v3.0.0
kind: DataContract
info:
title: orders
version: 1.2.0
owner: data-platform
schema:
- name: orders
physicalName: orders
physicalType: table
properties:
- name: order_id
logicalType: string
physicalType: uniqueidentifier
required: true
primaryKey: true
quality:
- rule: nullCount
dimension: completeness
severity: error
mustBe: "0"
- rule: duplicateCount
dimension: uniqueness
severity: error
mustBe: "0"
- name: status
logicalType: string
quality:
- rule: validValues
dimension: conformity
severity: error
mustBe: "['pending', 'paid', 'shipped', 'refunded']"
- name: total_amount
logicalType: number
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"
El YAML es la fuente de la verdad. Catalyst compila cada entrada quality al T-SQL correcto para el dialecto que se mostró antes, ejecuta el lote contra tu servidor y registra pass/warn/fail por comprobación. Editar una regla en el constructor visual reescribe ese mismo YAML, con los comentarios y el orden intactos, así que el contrato revisado en un pull request es el contrato que se ejecuta.
Fíjate en el campo severity. error hace fallar la ejecución; warning registra el incumplimiento y sigue adelante. Las comprobaciones de patrón sobre campos introducidos por personas suelen ser warnings: lo que quieres es la tendencia, no un aviso de guardia a las 3 de la madrugada.
Programación y alertas
Una validación que ejecutas a mano es una validación que ejecutas una sola vez. Apunta una programación al dataset —cada hora para la frescura en una tabla de streaming, a diario después de la ventana de ELT para todo lo demás— y alerta sobre la transición, no sobre el estado. Lo que importa es “orders ha empezado a fallar”, no “orders sigue fallando”, que es justo lo que enseña a la gente a ignorar el canal.
Dos notas operativas específicas de SQL Server:
- Configura un timeout de consulta. Un
COUNT_BIG(*)de tabla completa sobre un heap sin índices de mil millones de filas se puede pasar una hora ejecutándose tan tranquilo. Catalyst aplica un timeout por comprobación y reporta la comprobación comoerror(distinto defail), para que un problema de infraestructura nunca parezca un problema de datos. - Vigila la caché de planes. Los escaneos de agregación sin parámetros son baratos de compilar pero caros de ejecutar. En tablas muy grandes, valida una partición en lugar de la tabla entera, o ejecuta contra una vista indexada filtrada.
Trampas de SQL Server que conviene conocer
| Trampa | Qué ocurre | Qué hacer |
|---|---|---|
Desbordamiento de COUNT(*) | Falla por encima de 2100 millones de filas | Usa COUNT_BIG(*) |
NOT IN con nulos | Filas excluidas en silencio | Añade IS NOT NULL |
GETDATE() | Hora local del servidor | Usa SYSUTCDATETIME() |
| Collation insensible a mayúsculas | 'PAID' pasa una comprobación de 'paid' | Fija la collation, o normaliza con COLLATE |
| Conversión implícita | WHERE varchar_col = 123 escanea la tabla entera | Compara tipos equivalentes |
Igualdad con float | Los límites de rango fallan por redondeo | Usa decimal para el dinero |
Preguntas frecuentes
¿Puedo validar datos en SQL Server sin escribir SQL?
Sí. Define la expectativa de forma declarativa —obligatorio, único, valores permitidos, rango numérico, patrón, antigüedad máxima— y deja que la herramienta la compile a T-SQL. Catalyst importa las columnas y los tipos desde INFORMATION_SCHEMA, sugiere un conjunto de reglas de partida a partir del esquema y genera las consultas. Solo escribes SQL para la lógica genuinamente a medida, mediante una regla customSql.
¿La validación de calidad de datos necesita acceso de escritura a mi base de datos?
No. Todas las comprobaciones de esta guía son un SELECT que devuelve un único número. Basta con un login db_datareader más VIEW DEFINITION. Catalyst se conecta en modo solo lectura y guarda únicamente metadatos y resultados de comprobaciones; las filas se quedan en tu servidor.
¿Cómo compruebo expresiones regulares en T-SQL?
No puedes hacerlo directamente: SQL Server no tiene operador de regex. Los patrones anclados y de longitud fija se traducen a LIKE con clases de caracteres del tipo [0-9]. Todo lo que use alternancia, grupos opcionales o repetición no acotada tiene que convertirse en una lista de valores permitidos, una función CLR o una comprobación de SQL personalizado.
¿Con qué frecuencia deben ejecutarse las validaciones?
Ajusta la programación a la cadencia de los propios datos y ejecuta la comprobación justo después de la carga que los produce. Las reglas de frescura sobre tablas de streaming piden ejecuciones cada hora; las tablas de dimensiones en batch, una única ejecución cuando termina el ELT nocturno. Ejecutarlas con más frecuencia de la que cambian los datos solo produce ruido.
¿Funciona esto con Azure SQL y Microsoft Fabric?
Azure SQL Database usa la misma superficie de T-SQL, así que todo lo de aquí se aplica sin cambios. El warehouse de Microsoft Fabric y su endpoint de SQL analytics también hablan T-SQL con la misma ausencia de regex: consulta la guía de Microsoft Fabric para las diferencias en torno a la autenticación con Entra ID y la sincronización de metadatos de OneLake.