Escribir CHECK que reflejen la regla de negocio
Gestiona NULL explícitamente, expresa estados válidos por fila y conoce los límites de CHECK antes de confiarle la integridad de SQL Server.
Una restricción llamada PositiveQuantity parece garantizar cantidades positivas en todas las filas. Si contiene Quantity > 0 y la columna permite NULL, la promesa está incompleta. SQL Server rechaza FALSE, mientras UNKNOWN causado por NULL puede pasar. El nombre no añade semántica a la expresión.
Describir estados permitidos
La primera tabla demuestra el problema: insertar NULL funciona. Si la cantidad es obligatoria, combina NOT NULL con la condición positiva. Si desconocido es un estado válido, documéntalo expresamente en lugar de afirmar que CHECK exige un número.
La segunda tabla modela publicaciones. Un borrador no debe tener fecha de publicación y una fila publicada debe tenerla. Status es obligatorio y admite solo dos valores.
CREATE TABLE #WeakRule
(
ItemId int PRIMARY KEY,
Quantity int NULL CHECK (Quantity > 0)
);
INSERT #WeakRule VALUES (1, NULL);
SELECT ItemId, Quantity FROM #WeakRule;
CREATE TABLE #PublicationRule
(
ItemId int PRIMARY KEY,
Status varchar(10) NOT NULL
CHECK (Status IN ('Draft', 'Published')),
PublishedAt datetime2(0) NULL,
CHECK
(
(Status = 'Draft' AND PublishedAt IS NULL)
OR
(Status = 'Published' AND PublishedAt IS NOT NULL)
)
);
INSERT #PublicationRule VALUES
(1, 'Draft', NULL),
(2, 'Published', '20250115T12:00:00');
BEGIN TRY
INSERT #PublicationRule VALUES (3, 'Published', NULL);
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ConstraintError;
END CATCH;
SELECT * FROM #PublicationRule ORDER BY ItemId;
DROP TABLE #PublicationRule;
DROP TABLE #WeakRule;
La regla débil devuelve su fila NULL. La tabla de publicaciones acepta las dos primeras y rechaza la tercera. IS NULL e IS NOT NULL evitan UNKNOWN accidental en la relación entre columnas.
Antes de implementar una condición compuesta, escribe una tabla de verdad pequeña. Incluye Draft con y sin fecha, Published con y sin fecha, estado desconocido y estado NULL. Convierte esos casos en comprobaciones de migración o integración. Probar únicamente inserciones válidas deja sin examinar lo más importante.
Mantén legibles las expresiones. Una cadena larga de negaciones puede ser correcta y difícil de ampliar. Separa reglas independientes de dominio y relaciones entre columnas. Los nombres claros de restricciones persistentes facilitan diagnosticar errores.
Conocer la frontera de una regla por fila
CHECK sirve para invariantes basadas en la fila, como rangos permitidos o fecha final posterior a la inicial. No es un planificador que reevalúe registros cuando pasa el tiempo. Una condición con la hora actual se comprueba durante escrituras relevantes, no continuamente a medianoche.
Tampoco ocultes garantías globales de inventario o cantidades mínimas de filas dentro de una función escalar usada por CHECK. Otras filas pueden cambiar sin reevaluar la original, y las eliminaciones añaden problemas. Usa restricciones relacionales adecuadas o un diseño transaccional que proteja realmente la regla compartida.
Una clave externa expresa pertenencia a otra tabla mejor que una función de búsqueda. Una restricción única asegura unicidad mejor que contar duplicados aparentes. Una regla como 'solo una asignación actual' puede necesitar un índice único filtrado, no solo validar fechas de cada fila.
Las restricciones tampoco sustituyen autorización. Una fila estructuralmente correcta puede pertenecer a un tenant que el llamador no puede modificar. Mantén ambas comprobaciones en el contrato de escritura.
Introducir la regla sin esconder errores antiguos
Añadir una restricción comprobada exige que los datos existentes la cumplan. Audita las violaciones y decide su corrección o aislamiento. Rellenar valores ausentes con datos inventados solo para superar la migración altera el significado del negocio.
Una restricción puede estar habilitada pero no ser confiable si no se validaron las filas anteriores. Consulta is_disabled e is_not_trusted en sys.check_constraints. Para una restricción existente, WITH CHECK CHECK CONSTRAINT permite validar datos y establecer confianza. Planifica el trabajo y los bloqueos en tablas grandes.
Prueba también UPDATE. Cambiar Draft a Published debe establecer PublishedAt en la misma instrucción. Actualizar primero estado y después fecha produce una fila intermedia inválida. Ese fallo es útil porque exige una transición coherente.
Decide finalmente cómo presentar errores de restricciones al usuario. La validación temprana mejora la experiencia y la base mantiene la defensa final. Si después aparece Archived, actualiza conjuntamente modelo de estados, migración y pruebas. Debilitar la condición hasta que el código nuevo funcione no garantiza conservar la regla correcta.
Referencias técnicas: Microsoft Learn: CHECK constraints · Microsoft Learn: sys.check_constraints.