restricciones-constraints-sql-server

Constraints en SQL Server

  • 5 min

Las Constraints son reglas que el motor de base de datos aplica automáticamente para garantizar la Integridad de los datos.

Una base de datos no es un simple cubo donde tiramos información a lo loco. Si fuera así, acabaríamos con precios negativos, fechas de nacimiento en el futuro o usuarios duplicados.

Para evitar que esto no ocurra, SQL Server nos proporciona las Constraints. Si una instrucción INSERT o UPDATE incumple una de estas reglas, SQL Server detendrá la operación y devolverá un error.

Es la primera y más importante línea de defensa de nuestra aplicación. Vamos a ver las cinco imprescindibles.

PRIMARY KEY (Clave Primaria)

De esta ya hemos hablado, pero es tan importante que merece repasarse. La Primary Key (PK) es la columna (o conjunto de columnas) que identifica de forma inequívoca a cada fila de la tabla.

  • No permite valores NULL.
  • No permite valores duplicados.
  • Solo puede haber una por tabla.
CREATE TABLE Clientes (
    ClienteID INT PRIMARY KEY, -- Forma abreviada
    Nombre NVARCHAR(100)
);
Copied!

Internamente, al crear una Primary Key, SQL Server crea automáticamente un Índice Clustered (por defecto), lo que ordena físicamente los datos en el disco según esta clave para acelerar las búsquedas.

FOREIGN KEY (Clave Foránea)

La Foreign Key (FK) es la encargada de mantener la Integridad Referencial. Es la restricción que asegura que el valor de una columna coincida con un valor existente en la Primary Key de otra tabla.

Básicamente, impide que creemos “datos huérfanos”.

  • No puedes crear un Pedido para un cliente que no existe.
  • No puedes borrar un Cliente si tiene Pedidos (a menos que configures acciones en cascada).
CREATE TABLE Pedidos (
    PedidoID INT PRIMARY KEY,
    ClienteID INT,
    -- Definición de la restricción FK
    CONSTRAINT FK_Pedidos_Clientes FOREIGN KEY (ClienteID) 
    REFERENCES Clientes(ClienteID)
);
Copied!

Acostumbraos a poner nombre a vuestras constraints (como FK_Pedidos_Clientes). Si no lo hacéis, SQL Server le pondrá un nombre aleatorio horrible (tipo FK__Pedidos__Clie__1BFD2C07), y será una pesadilla administrarla en el futuro.

UNIQUE (Unicidad)

A veces necesitamos que una columna sea única, pero no es la clave primaria (ejemplo clásicos son el Email o el DNI).

La restricción UNIQUE asegura que no haya dos filas con el mismo valor en esa columna.

Diferencias con Primary Key:

  1. Puedes tener varias restricciones UNIQUE en una misma tabla.
  2. La restricción UNIQUE permite un valor NULL (en SQL Server), mientras que la PK no permite ninguno.
CREATE TABLE Usuarios (
    UsuarioID INT PRIMARY KEY,
    Email VARCHAR(100) UNIQUE, -- No puede haber dos emails iguales
    DNI VARCHAR(20) CONSTRAINT UQ_Usuarios_DNI UNIQUE -- Con nombre explícito
);
Copied!

CHECK (Validación lógica)

La restricción CHECK es la menos conocidas, pero una de las más potentes. Nos permite definir una expresión lógica (booleana) que cada fila debe cumplir para ser válida.

CREATE TABLE Productos (
    ProductoID INT PRIMARY KEY,
    Precio DECIMAL(10, 2),
    Stock INT,
    
    CONSTRAINT CK_Precio_Positivo CHECK (Precio > 0),
    CONSTRAINT CK_Stock_NoNegativo CHECK (Stock >= 0)
);
Copied!

Es perfecta para reglas de negocio simples:

  • “El precio debe ser mayor que cero”.
  • “La edad debe ser mayor o igual a 18”.
  • “El estado debe ser ‘Activo’, ‘Pausado’ o ‘Borrado’”.

DEFAULT (Valor por defecto)

Aunque técnicamente no restringe qué datos entran, la restricción DEFAULT es muy importante para la integridad. Define qué valor se guardará en una columna si no especificamos ninguno al hacer el INSERT.

CREATE TABLE LogEventos (
    EventoID INT PRIMARY KEY,
    Mensaje NVARCHAR(MAX),
    FechaEvento DATETIME DEFAULT GETDATE(), -- Si no se indica, pone la fecha actual
    Procesado BIT DEFAULT 0                 -- Por defecto, no procesado (false)
);
Copied!

Es muy útil para:

  • Fechas de creación (GETDATE()).
  • Estados iniciales (ej: Activo = 1).
  • Valores numéricos a cero en lugar de NULL.

Ejemplo completo

Vamos a ver cómo se ve una tabla que utiliza todas estas restricciones juntas y con una buena nomenclatura.

CREATE TABLE Empleados (
    -- 1. PRIMARY KEY con IDENTITY
    EmpleadoID INT IDENTITY(1,1),
    
    -- Restricción de nulabilidad (NOT NULL)
    Nombre NVARCHAR(50) NOT NULL,
    Apellido NVARCHAR(50) NOT NULL,
    
    -- 2. UNIQUE: El email corporativo no se puede repetir
    Email VARCHAR(100) NOT NULL,
    
    -- 3. CHECK: El salario debe ser lógico y la edad laboral permitida
    Salario DECIMAL(10, 2),
    Edad INT,
    
    -- 4. DEFAULT: Si no decimos nada, el empleado está activo al crearse
    Activo BIT CONSTRAINT DF_Empleados_Activo DEFAULT 1,
    FechaContratacion DATE DEFAULT GETDATE(),
    
    -- Definición de Constraints con nombre al final de la tabla
    CONSTRAINT PK_Empleados PRIMARY KEY (EmpleadoID),
    CONSTRAINT UQ_Empleados_Email UNIQUE (Email),
    CONSTRAINT CK_Empleados_Salario CHECK (Salario > 0),
    CONSTRAINT CK_Empleados_Edad CHECK (Edad >= 16)
);
Copied!

Al definir la tabla así, estamos creando una estructura autoprotegida. No importa si el programador de la aplicación se equivoca e intenta meter un salario negativo; la base de datos rechazará el dato y protegerá el negocio (y tu trabajo 😉).