SQL

Tutorial SQL: Restricciones e integridad

Aprende a usar PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK y DEFAULT para mantener la integridad y unicidad en bases de datos SQL.

Aprende SQL y certifícate

PRIMARY KEY y UNIQUE

Las restricciones son reglas que se aplican a las columnas de una tabla para mantener la integridad de los datos. Entre las restricciones más importantes en SQL se encuentran PRIMARY KEY y UNIQUE, que nos ayudan a garantizar la unicidad de los valores en determinadas columnas.

Restricción PRIMARY KEY

La restricción PRIMARY KEY identifica de manera única cada registro en una tabla. Actúa como un identificador que no puede repetirse ni contener valores nulos, lo que la convierte en la base para establecer relaciones entre tablas.

Puedes definir una PRIMARY KEY de dos formas principales:

  • 1. Al crear la tabla (forma más común):
CREATE TABLE clientes (
    id_cliente INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(100)
);
  • 2. Como restricción separada (útil para claves compuestas):
CREATE TABLE pedidos (
    id_pedido INT,
    id_producto INT,
    cantidad INT NOT NULL,
    PRIMARY KEY (id_pedido, id_producto)
);

En MySQL, también puedes usar la opción AUTO_INCREMENT para generar valores únicos automáticamente:

CREATE TABLE productos (
    id_producto INT PRIMARY KEY AUTO_INCREMENT,
    nombre VARCHAR(100) NOT NULL,
    precio DECIMAL(10,2)
);

En PostgreSQL, se utiliza la palabra clave SERIAL o IDENTITY:

CREATE TABLE productos (
    id_producto SERIAL PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    precio DECIMAL(10,2)
);

O con la sintaxis más moderna (PostgreSQL 10+):

CREATE TABLE productos (
    id_producto INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    precio DECIMAL(10,2)
);

Características importantes de PRIMARY KEY:

  • Solo puede existir una PRIMARY KEY por tabla
  • No permite valores NULL
  • Crea automáticamente un índice único para mejorar el rendimiento de las consultas
  • Es la base para establecer relaciones entre tablas mediante FOREIGN KEY

Restricción UNIQUE

La restricción UNIQUE garantiza que todos los valores en una columna (o conjunto de columnas) sean diferentes. A diferencia de PRIMARY KEY, una tabla puede tener múltiples restricciones UNIQUE.

  • 1. Al crear la tabla:
CREATE TABLE usuarios (
    id_usuario INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE,
    nombre_usuario VARCHAR(50) UNIQUE
);
  • 2. Como restricción separada:
CREATE TABLE empleados (
    id_empleado INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(100),
    numero_empleado VARCHAR(20),
    UNIQUE (email),
    UNIQUE (numero_empleado)
);
  • 3. Restricción UNIQUE con nombre personalizado:
CREATE TABLE productos (
    id_producto INT PRIMARY KEY,
    codigo_barras VARCHAR(13),
    sku VARCHAR(20),
    CONSTRAINT uk_codigo_barras UNIQUE (codigo_barras),
    CONSTRAINT uk_sku UNIQUE (sku)
);
  • 4. Restricción UNIQUE para múltiples columnas:
CREATE TABLE inscripciones (
    id_inscripcion INT PRIMARY KEY,
    id_estudiante INT,
    id_curso INT,
    fecha_inscripcion DATE,
    UNIQUE (id_estudiante, id_curso)
);

Características importantes de UNIQUE:

  • Una tabla puede tener múltiples restricciones UNIQUE
  • A diferencia de PRIMARY KEY, permite valores NULL (aunque solo uno, ya que NULL no se considera igual a otro NULL en este contexto)
  • También crea un índice único para mejorar el rendimiento
  • Es útil para columnas que requieren unicidad pero no son la clave principal

Diferencias entre PRIMARY KEY y UNIQUE

Característica PRIMARY KEY UNIQUE
Valores NULL No permitidos Permitidos (máximo uno)
Cantidad por tabla Solo una Múltiples
Índice Crea índice automáticamente Crea índice automáticamente
Uso principal Identificador principal Garantizar unicidad en columnas secundarias

Modificar restricciones en tablas existentes

Si necesitas agregar estas restricciones a tablas ya existentes, puedes usar ALTER TABLE:

  • Agregar PRIMARY KEY:
ALTER TABLE productos
ADD PRIMARY KEY (id_producto);
  • Agregar UNIQUE:
ALTER TABLE usuarios
ADD CONSTRAINT uk_email UNIQUE (email);
  • Eliminar restricciones:
-- Eliminar PRIMARY KEY
ALTER TABLE productos
DROP PRIMARY KEY;

-- Eliminar UNIQUE
ALTER TABLE usuarios
DROP CONSTRAINT uk_email;

Buenas prácticas

  • Nombra tus restricciones: Usar nombres descriptivos facilita el mantenimiento.
  • Usa PRIMARY KEY para identificadores principales: Cada tabla debería tener una clave primaria.
  • Usa UNIQUE para datos que no deben duplicarse: Como emails, nombres de usuario o códigos de producto.
  • Considera claves compuestas cuando sea apropiado: A veces la unicidad depende de la combinación de varias columnas.
  • Prefiere tipos de datos numéricos para las claves primarias por razones de rendimiento.

Ejemplo práctico

Veamos un ejemplo completo de una base de datos para una biblioteca:

-- Tabla de autores
CREATE TABLE autores (
    id_autor INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE,
    fecha_nacimiento DATE
);

-- Tabla de libros
CREATE TABLE libros (
    isbn VARCHAR(13) PRIMARY KEY,
    titulo VARCHAR(200) NOT NULL,
    id_autor INT NOT NULL,
    anio_publicacion INT,
    codigo_interno VARCHAR(20) UNIQUE
);

-- Tabla de préstamos (con clave primaria compuesta)
CREATE TABLE prestamos (
    id_usuario INT,
    isbn VARCHAR(13),
    fecha_prestamo DATE NOT NULL,
    fecha_devolucion DATE,
    PRIMARY KEY (id_usuario, isbn, fecha_prestamo)
);

En este ejemplo:

  • Cada autor tiene un ID único y un email único
  • Cada libro se identifica por su ISBN único y también tiene un código interno único
  • Los préstamos se identifican por la combinación de usuario, libro y fecha

Las restricciones PRIMARY KEY y UNIQUE son fundamentales para mantener la integridad de los datos en cualquier base de datos relacional. Proporcionan la base para las relaciones entre tablas y garantizan que los datos críticos no se dupliquen accidentalmente.

FOREIGN KEY

La restricción FOREIGN KEY (clave foránea) es un componente esencial en el diseño de bases de datos relacionales que establece y mantiene la integridad referencial entre tablas. Mientras que PRIMARY KEY y UNIQUE se centran en la unicidad de los datos, FOREIGN KEY garantiza que las relaciones entre tablas sean válidas y consistentes.

Concepto y propósito

Una clave foránea es un campo (o conjunto de campos) en una tabla que hace referencia a la clave primaria de otra tabla. Esta restricción crea una relación padre-hijo entre las tablas, donde:

  • La tabla que contiene la clave foránea se denomina tabla "hija" o "referenciadora"
  • La tabla a la que hace referencia (que contiene la clave primaria) se denomina tabla "padre" o "referenciada"

El propósito principal de una FOREIGN KEY es prevenir acciones que destruirían los vínculos entre tablas, asegurando que:

  1. Solo se puedan insertar en la tabla hija valores que existan en la tabla padre
  2. Los cambios en la tabla padre se gestionen adecuadamente en la tabla hija

Sintaxis básica

La sintaxis para crear una restricción FOREIGN KEY puede variar ligeramente entre MySQL y PostgreSQL, pero el concepto es el mismo:

  • Al crear una tabla:
CREATE TABLE pedidos (
    id_pedido INT PRIMARY KEY,
    id_cliente INT,
    fecha_pedido DATE NOT NULL,
    total DECIMAL(10,2),
    FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente)
);
  • Con nombre de restricción explícito:
CREATE TABLE pedidos (
    id_pedido INT PRIMARY KEY,
    id_cliente INT,
    fecha_pedido DATE NOT NULL,
    total DECIMAL(10,2),
    CONSTRAINT fk_cliente FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente)
);
  • Añadir a una tabla existente:
ALTER TABLE pedidos
ADD CONSTRAINT fk_cliente FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente);

Acciones referenciales

Uno de los aspectos más potentes de las claves foráneas es la capacidad de definir acciones referenciales que determinan qué sucede cuando se actualiza o elimina un registro en la tabla padre. Estas acciones se especifican mediante las cláusulas ON DELETE y ON UPDATE:

CREATE TABLE detalles_pedido (
    id_pedido INT,
    id_producto INT,
    cantidad INT NOT NULL,
    precio_unitario DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (id_pedido, id_producto),
    FOREIGN KEY (id_pedido) REFERENCES pedidos(id_pedido)
        ON DELETE CASCADE
        ON UPDATE RESTRICT
);

Las opciones disponibles para estas acciones son:

  • CASCADE: Propaga la acción (eliminación o actualización) a los registros relacionados en la tabla hija
  • RESTRICT: Impide la acción en la tabla padre si existen registros relacionados en la tabla hija
  • NO ACTION: Similar a RESTRICT, pero se verifica al final de la transacción (comportamiento predeterminado en muchos sistemas)
  • SET NULL: Establece como NULL los valores de la clave foránea en la tabla hija
  • SET DEFAULT: Establece los valores de la clave foránea en la tabla hija a sus valores predeterminados

Ejemplo de diferentes acciones referenciales:

-- Eliminar productos elimina automáticamente sus líneas de pedido
FOREIGN KEY (id_producto) REFERENCES productos(id_producto) ON DELETE CASCADE

-- Impedir la eliminación de clientes que tienen pedidos
FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente) ON DELETE RESTRICT

-- Al eliminar una categoría, los productos quedan sin categoría
FOREIGN KEY (id_categoria) REFERENCES categorias(id_categoria) ON DELETE SET NULL

Claves foráneas compuestas

Al igual que las claves primarias, las claves foráneas pueden estar compuestas por múltiples columnas:

CREATE TABLE reservas_asientos (
    id_vuelo VARCHAR(10),
    numero_asiento VARCHAR(5),
    id_pasajero INT,
    PRIMARY KEY (id_vuelo, numero_asiento),
    FOREIGN KEY (id_vuelo, numero_asiento) REFERENCES asientos_avion(id_vuelo, numero_asiento)
);

En este caso, la combinación de id_vuelo y numero_asiento debe existir en la tabla asientos_avion.

Consideraciones de rendimiento

Las claves foráneas tienen implicaciones importantes en el rendimiento:

  • Verificación de integridad: Cada operación INSERT o UPDATE en la tabla hija requiere una verificación en la tabla padre
  • Índices: Es recomendable que las columnas de clave foránea estén indexadas para mejorar el rendimiento de estas verificaciones
  • Operaciones en lote: Las operaciones masivas pueden ser más lentas debido a las comprobaciones de integridad

MySQL crea automáticamente un índice en las columnas de clave foránea, mientras que PostgreSQL requiere que lo crees manualmente:

-- En PostgreSQL, es recomendable crear un índice para la clave foránea
CREATE INDEX idx_pedidos_cliente ON pedidos(id_cliente);

Desactivación temporal de restricciones

En algunas situaciones, como migraciones de datos o cargas masivas, puede ser útil desactivar temporalmente las restricciones de clave foránea:

En MySQL:

-- Desactivar verificación de claves foráneas
SET FOREIGN_KEY_CHECKS = 0;

-- Realizar operaciones...

-- Reactivar verificación
SET FOREIGN_KEY_CHECKS = 1;

En PostgreSQL:

-- Desactivar restricciones para una tabla específica
ALTER TABLE pedidos DISABLE TRIGGER ALL;

-- Realizar operaciones...

-- Reactivar restricciones
ALTER TABLE pedidos ENABLE TRIGGER ALL;

Ejemplo práctico: Sistema de comercio electrónico

Veamos un ejemplo completo de un esquema básico para un sistema de comercio electrónico:

-- Tabla de clientes
CREATE TABLE clientes (
    id_cliente INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE,
    fecha_registro DATE NOT NULL
);

-- Tabla de categorías de productos
CREATE TABLE categorias (
    id_categoria INT PRIMARY KEY,
    nombre VARCHAR(50) NOT NULL,
    descripcion TEXT
);

-- Tabla de productos
CREATE TABLE productos (
    id_producto INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    precio DECIMAL(10,2) NOT NULL,
    stock INT NOT NULL DEFAULT 0,
    id_categoria INT,
    CONSTRAINT fk_categoria FOREIGN KEY (id_categoria) 
        REFERENCES categorias(id_categoria)
        ON DELETE SET NULL
);

-- Tabla de pedidos
CREATE TABLE pedidos (
    id_pedido INT PRIMARY KEY,
    id_cliente INT NOT NULL,
    fecha_pedido TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    estado VARCHAR(20) NOT NULL,
    CONSTRAINT fk_cliente FOREIGN KEY (id_cliente) 
        REFERENCES clientes(id_cliente)
        ON DELETE RESTRICT
);

-- Tabla de detalles de pedido
CREATE TABLE detalles_pedido (
    id_pedido INT,
    id_producto INT,
    cantidad INT NOT NULL,
    precio_unitario DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (id_pedido, id_producto),
    CONSTRAINT fk_pedido FOREIGN KEY (id_pedido) 
        REFERENCES pedidos(id_pedido)
        ON DELETE CASCADE,
    CONSTRAINT fk_producto FOREIGN KEY (id_producto) 
        REFERENCES productos(id_producto)
        ON DELETE RESTRICT
);

En este ejemplo:

  • Un cliente puede realizar múltiples pedidos (relación uno a muchos)
  • Un producto pertenece a una categoría (relación muchos a uno)
  • Un pedido contiene múltiples productos y un producto puede estar en múltiples pedidos (relación muchos a muchos resuelta mediante la tabla detalles_pedido)
  • Si se elimina un cliente, se impide la operación si tiene pedidos (RESTRICT)
  • Si se elimina una categoría, los productos quedan sin categoría (SET NULL)
  • Si se elimina un pedido, se eliminan automáticamente sus detalles (CASCADE)
  • Si se intenta eliminar un producto que está en algún pedido, se impide la operación (RESTRICT)

Buenas prácticas

  • Nombra tus restricciones: Usa prefijos como fk_ para identificar fácilmente las claves foráneas.
  • Considera cuidadosamente las acciones referenciales: Elige CASCADE, RESTRICT, SET NULL según la lógica de negocio.
  • Indexa las columnas de clave foránea: Mejora el rendimiento de las consultas y verificaciones.
  • Mantén la coherencia de tipos de datos: La clave foránea debe tener el mismo tipo de datos que la clave primaria a la que hace referencia.
  • Documenta el modelo de datos: Las relaciones entre tablas son fundamentales para entender la estructura de la base de datos.

Las claves foráneas son una herramienta fundamental para mantener la integridad referencial en bases de datos relacionales, asegurando que las relaciones entre entidades sean válidas y consistentes a lo largo del tiempo.

CHECK y DEFAULT

Las restricciones CHECK y DEFAULT son mecanismos fundamentales para mantener la integridad de los datos en bases de datos relacionales. Mientras que PRIMARY KEY, UNIQUE y FOREIGN KEY controlan la unicidad y las relaciones entre tablas, CHECK y DEFAULT se centran en validar y proporcionar valores predeterminados para las columnas.

Restricción CHECK

La restricción CHECK permite definir condiciones personalizadas que deben cumplir los valores de una columna o conjunto de columnas. Actúa como un guardián que valida los datos antes de permitir su inserción o actualización en la tabla.

Sintaxis básica

La forma más común de definir una restricción CHECK es durante la creación de la tabla:

CREATE TABLE productos (
    id_producto INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    precio DECIMAL(10,2) CHECK (precio > 0),
    stock INT CHECK (stock >= 0)
);

También puedes definir la restricción con un nombre explícito para facilitar su gestión:

CREATE TABLE empleados (
    id_empleado INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    salario DECIMAL(10,2),
    departamento VARCHAR(50),
    CONSTRAINT chk_salario_positivo CHECK (salario > 0)
);

Las restricciones CHECK pueden involucrar múltiples columnas y expresiones complejas:

CREATE TABLE pedidos (
    id_pedido INT PRIMARY KEY,
    fecha_pedido DATE NOT NULL,
    fecha_entrega DATE,
    estado VARCHAR(20),
    CONSTRAINT chk_fechas CHECK (fecha_entrega >= fecha_pedido),
    CONSTRAINT chk_estado CHECK (estado IN ('Pendiente', 'Enviado', 'Entregado', 'Cancelado'))
);

Añadir restricciones CHECK a tablas existentes

Puedes agregar restricciones CHECK a tablas ya creadas mediante ALTER TABLE:

ALTER TABLE productos
ADD CONSTRAINT chk_precio_minimo CHECK (precio >= 1.99);

Es importante tener en cuenta que al añadir una restricción CHECK a una tabla con datos existentes, estos serán validados contra la nueva restricción. Si algún registro no cumple la condición, la operación fallará.

Ejemplos prácticos de restricciones CHECK

  • Validación de rangos numéricos:
CREATE TABLE calificaciones (
    id_estudiante INT,
    id_curso INT,
    calificacion DECIMAL(4,2),
    PRIMARY KEY (id_estudiante, id_curso),
    CONSTRAINT chk_calificacion CHECK (calificacion >= 0 AND calificacion <= 10)
);
  • Validación de formatos y patrones (PostgreSQL):
CREATE TABLE usuarios (
    id_usuario INT PRIMARY KEY,
    email VARCHAR(100) NOT NULL,
    codigo_postal VARCHAR(5),
    CONSTRAINT chk_email CHECK (email LIKE '%@%.%'),
    CONSTRAINT chk_cp CHECK (codigo_postal ~ '^[0-9]{5}$')
);
  • Validación de relaciones entre columnas:
CREATE TABLE productos_descuento (
    id_producto INT PRIMARY KEY,
    precio_regular DECIMAL(10,2) NOT NULL,
    precio_descuento DECIMAL(10,2),
    CONSTRAINT chk_descuento CHECK (precio_descuento IS NULL OR precio_descuento < precio_regular)
);

Restricción DEFAULT

La restricción DEFAULT especifica un valor predeterminado para una columna cuando no se proporciona un valor explícito en una operación INSERT. Es especialmente útil para establecer valores comunes, marcas de tiempo o estados iniciales.

Sintaxis básica

CREATE TABLE usuarios (
    id_usuario INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    activo BOOLEAN DEFAULT TRUE,
    fecha_registro DATE DEFAULT CURRENT_DATE
);

En este ejemplo, si no se especifica un valor para activo o fecha_registro al insertar un nuevo usuario, se utilizarán automáticamente los valores predeterminados.

Ejemplos de valores DEFAULT comunes

  • Valores booleanos:
CREATE TABLE configuraciones (
    id_configuracion INT PRIMARY KEY,
    nombre VARCHAR(50) NOT NULL,
    habilitada BOOLEAN DEFAULT FALSE
);
  • Fechas y horas actuales:

En MySQL:

CREATE TABLE registros (
    id_registro INT PRIMARY KEY,
    descripcion TEXT,
    creado_en DATETIME DEFAULT CURRENT_TIMESTAMP,
    actualizado_en DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

En PostgreSQL:

CREATE TABLE registros (
    id_registro SERIAL PRIMARY KEY,
    descripcion TEXT,
    creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    actualizado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  • Valores numéricos y textuales:
CREATE TABLE productos (
    id_producto INT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    precio DECIMAL(10,2) NOT NULL,
    descuento DECIMAL(5,2) DEFAULT 0.00,
    unidad VARCHAR(20) DEFAULT 'unidad',
    disponible BOOLEAN DEFAULT TRUE
);
  • Expresiones y funciones (PostgreSQL):
CREATE TABLE documentos (
    id_documento SERIAL PRIMARY KEY,
    titulo VARCHAR(200) NOT NULL,
    contenido TEXT,
    version INT DEFAULT 1,
    hash_contenido VARCHAR(64) DEFAULT md5(random()::text)
);

Modificar valores DEFAULT en tablas existentes

Puedes cambiar o añadir valores predeterminados a columnas existentes:

-- Añadir un valor predeterminado
ALTER TABLE usuarios
ALTER COLUMN ultimo_acceso SET DEFAULT CURRENT_TIMESTAMP;

-- Eliminar un valor predeterminado
ALTER TABLE usuarios
ALTER COLUMN nivel_acceso DROP DEFAULT;

Combinando CHECK y DEFAULT

Las restricciones CHECK y DEFAULT a menudo se utilizan juntas para garantizar tanto la validez como la completitud de los datos:

CREATE TABLE articulos (
    id_articulo INT PRIMARY KEY,
    titulo VARCHAR(200) NOT NULL,
    contenido TEXT,
    estado VARCHAR(20) DEFAULT 'Borrador' CHECK (estado IN ('Borrador', 'Publicado', 'Archivado')),
    visitas INT DEFAULT 0 CHECK (visitas >= 0),
    fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    fecha_publicacion TIMESTAMP,
    CONSTRAINT chk_fechas CHECK (fecha_publicacion IS NULL OR fecha_publicacion >= fecha_creacion)
);

En este ejemplo:

  • Los artículos comienzan en estado 'Borrador' por defecto
  • El contador de visitas comienza en 0
  • Se registra automáticamente la fecha de creación
  • Se verifica que la fecha de publicación (si existe) sea posterior a la de creación

Diferencias entre MySQL y PostgreSQL

Aunque la sintaxis básica es similar, existen algunas diferencias importantes:

MySQL

  • Las restricciones CHECK fueron ignoradas en versiones anteriores a MySQL 8.0.16
  • Soporta ON UPDATE CURRENT_TIMESTAMP para actualizar automáticamente columnas de tipo fecha/hora
  • Tiene limitaciones en las expresiones que pueden usarse en restricciones CHECK
-- Ejemplo específico de MySQL
CREATE TABLE productos (
    id_producto INT PRIMARY KEY,
    nombre VARCHAR(100),
    precio DECIMAL(10,2),
    ultima_actualizacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT chk_precio CHECK (precio > 0)
);

PostgreSQL

  • Soporta CHECK desde hace mucho tiempo con funcionalidad completa
  • Permite expresiones más complejas, incluyendo subconsultas en algunas situaciones
  • Ofrece tipos de datos y operadores adicionales para validaciones avanzadas
-- Ejemplo específico de PostgreSQL
CREATE TABLE productos (
    id_producto SERIAL PRIMARY KEY,
    nombre VARCHAR(100),
    precio DECIMAL(10,2),
    ultima_actualizacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT chk_precio CHECK (precio > 0)
);

-- Crear una función trigger para actualizar el timestamp
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.ultima_actualizacion = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_producto_timestamp
BEFORE UPDATE ON productos
FOR EACH ROW EXECUTE FUNCTION update_modified_column();

Ejemplo práctico: Sistema de gestión de inventario

Veamos un ejemplo completo que utiliza CHECK y DEFAULT para un sistema de gestión de inventario:

CREATE TABLE categorias (
    id_categoria SERIAL PRIMARY KEY,
    nombre VARCHAR(50) NOT NULL,
    activa BOOLEAN DEFAULT TRUE
);

CREATE TABLE productos (
    id_producto SERIAL PRIMARY KEY,
    codigo VARCHAR(20) UNIQUE NOT NULL,
    nombre VARCHAR(100) NOT NULL,
    descripcion TEXT,
    precio_compra DECIMAL(10,2) NOT NULL CHECK (precio_compra >= 0),
    precio_venta DECIMAL(10,2) NOT NULL CHECK (precio_venta >= 0),
    stock_actual INT DEFAULT 0 CHECK (stock_actual >= 0),
    stock_minimo INT DEFAULT 5 CHECK (stock_minimo >= 0),
    id_categoria INT,
    fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    descontinuado BOOLEAN DEFAULT FALSE,
    CONSTRAINT chk_precios CHECK (precio_venta > precio_compra),
    CONSTRAINT fk_categoria FOREIGN KEY (id_categoria) REFERENCES categorias(id_categoria)
);

CREATE TABLE movimientos_inventario (
    id_movimiento SERIAL PRIMARY KEY,
    id_producto INT NOT NULL,
    tipo_movimiento VARCHAR(20) NOT NULL DEFAULT 'Entrada' CHECK (tipo_movimiento IN ('Entrada', 'Salida', 'Ajuste')),
    cantidad INT NOT NULL CHECK (cantidad > 0),
    fecha_movimiento TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    motivo VARCHAR(100) DEFAULT 'Operación regular',
    CONSTRAINT fk_producto FOREIGN KEY (id_producto) REFERENCES productos(id_producto)
);

En este ejemplo:

  • Las categorías están activas por defecto
  • Los productos tienen precios de compra y venta que deben ser positivos
  • El precio de venta debe ser mayor que el precio de compra
  • El stock inicial es 0 y no puede ser negativo
  • Cada producto tiene un stock mínimo predeterminado de 5 unidades
  • Los movimientos de inventario pueden ser de entrada, salida o ajuste
  • Todos los movimientos registran automáticamente la fecha y hora

Buenas prácticas

  • Usa nombres descriptivos para las restricciones: Facilita la identificación de errores y el mantenimiento.
  • Combina CHECK con NOT NULL cuando sea necesario: CHECK (precio > 0) no impide valores NULL, usa también NOT NULL si quieres evitarlos.
  • Mantén las restricciones simples: Divide validaciones complejas en múltiples restricciones CHECK para facilitar la depuración.
  • Considera el rendimiento: Las restricciones CHECK muy complejas pueden afectar el rendimiento de las operaciones INSERT y UPDATE.
  • Documenta el propósito de cada restricción: Especialmente para validaciones de negocio no obvias.
  • Usa DEFAULT para valores comunes: Reduce errores y simplifica las consultas de inserción.
  • Valida los datos antes de migrarlos: Al añadir restricciones a tablas existentes, verifica primero si los datos cumplen las condiciones.

Las restricciones CHECK y DEFAULT son herramientas esenciales para implementar reglas de negocio directamente en la estructura de la base de datos, garantizando que los datos sean no solo relacionalmente íntegros, sino también lógicamente válidos según los requisitos específicos de la aplicación.

Aprende SQL online

Otros ejercicios de programación de SQL

Evalúa tus conocimientos de esta lección Restricciones e integridad con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Tipos de datos

Test

Inserción de datos: INSERT INTO

Test

Filtrado de grupos de resultados con HAVING

Test

Uso de índices y particiones

Test

Renombrar tablas y bases de datos: RENAME

Test

Uso de vistas

Test

Uso de INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN

Test

Agrupación de resultados con GROUP BY

Test

Creación y uso de subqueries

Test

Sentencias INSERT

Código

Copias de seguridad y restauración de bases de datos

Test

Uso de INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN

Código

Instalación de MySQL

Test

Relaciones entre tablas

Código

Eliminación de datos: DELETE

Test

Creación de bases de datos y tablas: CREATE DATABASE, CREATE TABLE

Test

Creación y uso de funciones

Test

Creación de tablas e inserción de datos con SQL

Proyecto

Uso de funciones agregadas: COUNT, SUM, AVG, MAX, MIN

Test

Optimización de consultas

Test

Introducción a SQL

Test

Triggers y eventos

Test

Clasificación de resultados con ORDER BY

Test

Alterar la estructura de tablas existentes: ALTER TABLE

Test

Eliminación de datos: DELETE

Código

Instalación de PostgreSQL

Test

Creación y uso de procedimientos almacenados

Test

Consultas básicas de selección: SELECT y WHERE

Test

Vaciar tablas y bases de datos: DROP

Test

Actualización de datos: UPDATE

Test

Creación y manejo de usuarios y roles

Test

Consultas básicas de selección SELECT y WHERE

Código

Creación de bases de datos y tablas

Código

Bases de datos y tablas

Test

Actualización de datos: UPDATE

Código

Relaciones entre tablas

Test

Filtrado de valores únicos con DISTINCT

Test

Asignación y gestión de permisos

Test

Todas las lecciones de SQL

Accede a todas las lecciones de SQL y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Sql

Introducción Y Entorno

Ddl Y Dml

Introducción Y Entorno

Instalación De Mysql

Introducción Y Entorno

Instalación De Postgresql

Introducción Y Entorno

Tipos De Datos

Introducción Y Entorno

Bases De Datos Y Tablas

Introducción Y Entorno

Sistemas De Gestión De Bases De Datos

Introducción Y Entorno

Tipos De Bases De Datos

Introducción Y Entorno

Creación De Bases De Datos Y Tablas: Create Database, Create Table

Sintaxis Dml Crud

Consultas Básicas De Selección: Select Y Where

Sintaxis Dml Crud

Inserción De Datos: Insert Into

Sintaxis Dml Crud

Actualización De Datos: Update

Sintaxis Dml Crud

Eliminación De Datos: Delete

Sintaxis Dml Crud

Introducción A Dml

Sintaxis Dml Crud

Consultar Datos: Select

Sintaxis Dml Crud

Clasificación De Resultados Con Order By

Filtros Y Clasificación

Filtrado De Valores Únicos Con Distinct

Filtros Y Clasificación

Paginación Con Limit Y Offset

Filtros Y Clasificación

Alterar La Estructura De Tablas Existentes: Alter Table

Sintaxis Ddl

Renombrar Tablas Y Bases De Datos: Rename

Sintaxis Ddl

Vaciar Tablas Y Bases De Datos: Drop

Sintaxis Ddl

Uso De Funciones Agregadas: Count, Sum, Avg, Max, Min

Funciones Y Agrupación

Agrupación De Resultados Con Group By

Funciones Y Agrupación

Filtrado De Grupos De Resultados Con Having

Funciones Y Agrupación

Funciones Numéricas Y Matemáticas

Funciones Y Agrupación

Funciones De Fecha Y Hora

Funciones Y Agrupación

Funciones De Texto

Funciones Y Agrupación

Many To One

Asociaciones Entre Tablas

One To Many

Asociaciones Entre Tablas

One To One

Asociaciones Entre Tablas

Many To Many

Asociaciones Entre Tablas

Relaciones Entre Tablas

Joins Y Subqueries

Uso De Inner Join, Left Join, Right Join, Full Join

Joins Y Subqueries

Creación Y Uso De Subqueries

Joins Y Subqueries

Left Join Y Right Join

Joins Y Subqueries

Full Join

Joins Y Subqueries

Cross Join Y Self Join

Joins Y Subqueries

Optimización De Consultas

Sintaxis Avanzada

Uso De Índices Y Particiones

Sintaxis Avanzada

Uso De Vistas

Sintaxis Avanzada

Triggers Y Eventos

Sintaxis Avanzada

Particiones

Sintaxis Avanzada

Restricciones E Integridad

Sintaxis Avanzada

Transacciones

Sintaxis Avanzada

Vistas Materializadas

Sintaxis Avanzada

Rollback

Sintaxis Avanzada

Vistas Con Create View

Sintaxis Avanzada

Principios Acid

Sintaxis Avanzada

Manejo De Errores Y Excepciones

Sintaxis Avanzada

Funciones Ventana

Sintaxis Avanzada

Índices

Sintaxis Avanzada

Expresiones De Tabla Comunes (Cte) Con With

Sintaxis Avanzada

Creación Y Uso De Funciones

Programación En Sql

Creación Y Uso De Procedimientos Almacenados

Programación En Sql

Variables Y Control De Flujo

Programación En Sql

Creación Y Manejo De Usuarios Y Roles

Seguridad Y Administración

Asignación Y Gestión De Permisos

Seguridad Y Administración

Copias De Seguridad Y Restauración De Bases De Datos

Seguridad Y Administración

Accede GRATIS a SQL y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender la función y características de las restricciones PRIMARY KEY y UNIQUE para garantizar la unicidad de datos.
  • Aprender a definir y gestionar claves foráneas (FOREIGN KEY) para mantener la integridad referencial entre tablas.
  • Conocer el uso de restricciones CHECK para validar condiciones personalizadas en los datos.
  • Entender la utilidad de la restricción DEFAULT para asignar valores predeterminados en columnas.
  • Aplicar buenas prácticas en la definición y mantenimiento de restricciones para asegurar la integridad y rendimiento de la base de datos.