Course
Restricciones de integridad en SQL: Una guía con ejemplos
Imagina una base de datos hospitalaria en la que las alergias de los pacientes no pueden dejarse en blanco, o un sistema financiero en el que los importes de las transacciones deben ser números positivos. En estos escenarios y en otros innumerables, confiamos en las restricciones de integridad para garantizar que nuestros datos sigan siendo precisos, coherentes y fiables.
En SQL, las restricciones de integridad son reglas que imponemos a las tablas de nuestra base de datos para mantener la calidad de los datos. Ayudan a evitar errores, aplican las normas empresariales y garantizan que nuestros datos reflejen las entidades y relaciones del mundo real que representan.
En este artículo, profundizaremos en los tipos esenciales de restricciones de integridad en SQL, proporcionando explicaciones claras y ejemplos prácticos para ilustrar su uso en una base de datos PostgreSQL. Aunque utilizaremos la sintaxis PostgreSQL, los conceptos se trasladan fácilmente a otros dialectos SQL.
Si quieres aprender más sobre SQL, consulta esta lista de cursos de SQL.
¿Qué son las restricciones de integridad en SQL?
Considera un escenario en el que tenemos una tabla que almacena información de los usuarios de una aplicación web. Algunos datos, como la edad del usuario, pueden ser opcionales porque no impiden que los usuarios accedan a nuestra aplicación. Sin embargo, tener una contraseña para cada usuario es esencial para iniciar sesión. Para solucionar esto, implementaríamos una restricción de integridad en la columna contraseña de nuestra tabla de usuarios para garantizar que cada entrada de esta tabla incluya una contraseña.
En esencia, las restricciones de integridad son cruciales para:
- Evitar que falten datos.
- Asegurarse de que todos los datos se ajustan a los tipos y rangos de valores previstos.
- Mantener vínculos adecuados entre los datos de las distintas tablas.
En este artículo, exploraremos las siguientes restricciones de integridad esenciales en SQL:
PRIMARY KEY
: Identifica de forma única cada registro de una tabla.NOT NULL
: Garantiza que una columna no puede contener valores NULL.UNIQUE
: Garantiza que todos los valores de una columna o grupo de columnas sean únicos.DEFAULT
: Proporciona un valor por defecto para una columna cuando no se especifica ninguno.CHECK
: Garantiza que todos los valores de una columna cumplen una condición específica.FOREIGN KEY
: Establece relaciones entre tablas haciendo referencia a una clave primaria de otra tabla.
Caso de estudio: Base de datos universitaria
Consideremos una base de datos relacional de una universidad. Esta base de datos contiene tres tablas: students
, courses
, y enrollments
.
Mesa de estudiantes
La tabla students
contiene información sobre todos los estudiantes universitarios.
student_id
: El identificador del alumnofirst_name
: El nombre de pila del alumno.last_name
: El apellido del alumno.email
: La dirección de correo electrónico del alumno.major
: La especialidad del estudiante.enrollment_year
: El año en que se matriculó el alumno.
Tabla de cursos
La tabla courses
contiene información sobre los cursos disponibles en la universidad.
course_id
: El identificador del curso.course_name
: El nombre del curso.department
: El departamento de cursos.
Tabla de inscripciones
La tabla enrollments
almacena información sobre qué alumnos están matriculados en qué cursos.
student_id
: El identificador del alumno matriculado en el curso.course_id
: El identificador del curso.year
: El año de inscripción.grade
: La nota del alumno en este curso.is_passing_grade
: Un booleano que indica si la nota es de aprobado.
A lo largo de este artículo, utilizaremos esta base de datos de ejemplo y mostraremos varias formas de hacer cumplir la integridad de los datos. Utilizaremos la sintaxis PostgreSQL en nuestras consultas. Sin embargo, los conceptos se trasladan fácilmente a otros tipos de SQL.
PRIMARY KEY Restricción
La universidad pretende identificar a cada estudiante de forma única. Utilizar el nombre y los apellidos para este fin no es aconsejable debido a la posibilidad de duplicación de nombres entre los estudiantes. Del mismo modo, basarse en las direcciones de correo electrónico no es lo ideal, ya que los alumnos pueden cambiar sus correos electrónicos.
La solución habitual para garantizar una identificación única es asignar un identificador único a cada alumno, que almacenamos en la columna student_id
. Podemos aplicar una restricción PRIMARY KEY
a la columna student_id
para garantizar que cada alumno tiene un identificador único.
Esta restricción se define en el comando CREATE TABLE
, tras la especificación del tipo de datos de la columna:
CREATE TABLE students (
student_id INT PRIMARY KEY,
first_name TEXT,
last_name TEXT,
email TEXT,
major TEXT,
enrollment_year INT
);
La consulta anterior crea la tabla de alumnos con las seis columnas que hemos mencionado antes.
La restricción PRIMARY KEY
garantiza que:
- Cada alumno tiene un
student_id
. - Cada
student_id
es único.
Restricción PRIMARY KEY en varias columnas
En algunos casos, necesitamos utilizar varias columnas para identificar cada fila de forma única. Por ejemplo, la mesa enrollments
. Un estudiante puede matricularse en varios cursos, lo que da lugar a que varias filas compartan el mismo student_id
. Del mismo modo, un curso puede tener varios alumnos matriculados, lo que da lugar a numerosas filas con el mismo course_id
.
Dado que ningún campo puede identificar de forma única una fila, cada registro de inscripción se determina utilizando una combinación de student_id
, course_id
, y year
.
Cuando intervienen varias columnas, la restricción PRIMARY KEY
se especifica al final del comando CREATE TABLE
.
CREATE TABLE enrollments (
student_id INT,
course_id INT,
year INT,
grade INT,
is_passing_grade BOOLEAN,
PRIMARY KEY (student_id, course_id, year)
);
Restricciones de integridad tras la creación de la tabla
Hay dos formas de añadir restricciones de integridad. Acabamos de aprender a hacerlo cuando se crea la tabla.
Imagina que la tabla ya existe, pero has olvidado especificar la restricción PRIMARY KEY
. También puedes definirla después de crear la tabla mediante el comando ALTER TABLE
, de este modo:
ALTER TABLE enrollments
ADD CONSTRAINT enroll_pk
PRIMARY KEY (student_id, course_id, year);
En la consulta ALTER TABLE
, denominamos a la restricción enroll_pk
(que significa clave primaria de inscripción). Este nombre puede ser cualquier identificador de tu elección, pero se recomienda seleccionar un nombre que transmita sucintamente la finalidad de la restricción.
Es una buena práctica nombrar las restricciones de integridad, ya que proporciona varias ventajas:
- Permite una referencia más fácil, especialmente cuando necesites modificar o eliminar la restricción en el futuro.
- Permite gestionar y organizar las restricciones, sobre todo en bases de datos con un gran número de restricciones.
NOT NULL Restricción
La universidad quiere asegurarse de que el nombre y la dirección de correo electrónico de cada estudiante quedan registrados en la base de datos. No quieren que el personal se olvide de introducir accidentalmente alguno de esos campos.
Para solucionarlo, podemos utilizar las restricciones NOT NULL
en estas tres columnas al crear la tabla:
CREATE TABLE students (
student_id INT,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT NOT NULL,
major TEXT,
enrollment_year INT
);
La consulta anterior utiliza las restricciones NOT NULL
para garantizar que las columnas first_name
, last_name
y email
no pueden tener valores NULL
(indefinidos).
Puedes añadir una restricción NOT NULL
a una tabla existente utilizando el comando ALTER TABLE
. La sintaxis para añadir restricciones NOT NULL
es la siguiente:
ALTER TABLE students
ALTER COLUMN first_name SET NOT NULL,
ALTER COLUMN last_name SET NOT NULL,
ALTER COLUMN email SET NOT NULL;
Restricción ÚNICA
Las direcciones de correo electrónico son intrínsecamente únicas para cada persona. En situaciones en las que estemos seguros de que un campo nunca debe tener valores duplicados, es una buena práctica imponerlo a nivel de base de datos. Esto ayuda a evitar errores y a garantizar la integridad de los datos.
Añadir esta restricción se hace del mismo modo que las demás.
CREATE TABLE students (
...
email TEXT UNIQUE,
...
);
Sin embargo, en nuestro caso, también queremos hacer cumplir la restricción NOT NULL
. Podemos aplicar varias restricciones a una misma columna separándolas por espacios:
CREATE TABLE students (
...
email TEXT UNIQUE NOT NULL,
...
);
Ten en cuenta que el orden no importa, también podríamos haber utilizado NOT NULL UNIQUE
.
Añadir una restricción UNIQUE
a una tabla existente se hace del siguiente modo:
ALTER TABLE students
ADD CONSTRAINT unique_emails UNIQUE (email);
Restricción UNIQUE en varias columnas
Supongamos que queremos asegurarnos de que el nombre del curso de cada departamento es único en la tabla courses
. En este caso, las columnas course_name
y department
juntas deben ser únicas.
Cuando intervienen varias columnas, la restricción se añade al final del comando CREATE TABLE
:
CREATE TABLE courses (
course_id INT,
course_name TEXT,
department TEXT,
UNIQUE (course_name, department)
);
También podemos añadir la restricción modificando una tabla existente. En este caso, proporcionamos una tupla con los nombres de las columnas:
ALTER TABLE courses
ADD CONSTRAINT unique_course_name_department
UNIQUE (course_name, department);
Restricciones NOT NULL UNIQUE vs PRIMARY KEY
Hemos aprendido que una restricción PRIMARY KEY
impone tanto la unicidad como que no haya valores perdidos. Así que te estarás preguntando cuál es la diferencia entre:
course_id INT PRIMARY KEY
course_id INT UNIQUE NOT NULL
La diferencia entre NOT NULL UNIQUE
y PRIMARY KEY
en una mesa es su finalidad y uso.
Mientras que ambas imponen la unicidad y la no anulabilidad de una columna o columnas, una tabla sólo puede tener un PRIMARY KEY
, cuyo objetivo es identificar de forma única cada registro de la tabla.
Por otra parte, la restricción de combinación NOT NULL UNIQUE
puede aplicarse a columnas adicionales para imponer un valor único en cada fila, sin valores nulos, que sirva para mantener la integridad de los datos para reglas empresariales específicas. Una tabla puede tener cualquier número de restricciones NOT NULL UNIQUE
.
La existencia de ambos permite una mayor flexibilidad en el diseño de la base de datos, permitiendo múltiples formas de hacer cumplir la unicidad y la integridad de los datos, al tiempo que se distingue entre el identificador primario de un registro y otros atributos importantes y únicos dentro de una tabla.
Restricción DEFAULT
Los estudiantes pueden necesitar algún tiempo después de la inscripción en la universidad para elegir su especialidad. La universidad desea que el valor de la columna especialidad sea la cadena "No declarado" para los estudiantes que aún no han seleccionado su especialidad.
Para ello, podemos establecer un valor por defecto de esta columna mediante la restricción DEFAULT
. Podemos modificar la tabla de alumnos así
ALTER TABLE students
ALTER COLUMN major SET DEFAULT 'Undeclared';
Si, en cambio, queremos establecer una restricción DEFAULT
al crear la tabla, podemos hacerlo declarándola después del tipo de datos de la columna:
CREATE TABLE students (
...
major TEXT DEFAULT 'Undeclared',
...
);
Restricción CHECK
En esta universidad en concreto, las notas van de 0 a 100. Sin ninguna restricción, la columna grade
de la tabla enrollment
acepta cualquier valor entero. Podemos solucionarlo utilizando una restricción CHECK
para obligar a que los valores estén entre 0 y 100.
ALTER TABLE enrollments
ADD CONSTRAINT grade_range CHECK (grade BETWEEN 0 AND 100);
En general, las restricciones de CHECK
nos permiten validar condiciones específicas que queremos que cumplan los datos. Son importantes para garantizar la coherencia e integridad de los datos.
Una restricción CHECK
puede implicar a más de una columna. Utilicémoslo para asegurarnos de que grade
y is_passing_grade
tienen valores coherentes. Digamos que una nota es de aprobado si su valor es al menos 60. Entonces podemos asegurar que is_passing_grade
es TRUE
si y sólo si el grado es al menos 60. Hagámoslo en la creación de la tabla para mostrar cómo se declaran las restricciones CHECK
en el comando CREATE TABLE
:
CREATE TABLE enrollments (
...
grade INT,
is_passing_grade BOOLEAN,
CONSTRAINT grade_check CHECK (grade BETWEEN 0 AND 100),
CONSTRAINT is_passing_grade CHECK (
(grade >= 60 AND is_passing_grade = TRUE) OR
(grade < 60 AND is_passing_grade = FALSE)
)
);
Hay un problema con la restricción anterior. Cuando un alumno se matricula en un curso, todavía no tiene nota. Así que deberíamos permitir los valores de NULL
en el grado. Al hacerlo, también tenemos que actualizar la restricción de aprobado para que sea NULL
cuando la nota aún no esté definida. He aquí cómo actualizar la restricción para tenerlas en cuenta:
CREATE TABLE enrollments (
...
grade INT NULL DEFAULT NULL,
is_passing_grade BOOLEAN NULL DEFAULT NULL,
CONSTRAINT grade_check CHECK (grade BETWEEN 0 AND 100),
CONSTRAINT is_passing_grade CHECK (
(grade IS NULL AND is_passing_grade IS NULL) OR
(grade >= 60 AND is_passing_grade = TRUE) OR
(grade < 60 AND is_passing_grade = FALSE)
),
...
);
Observa que tanto en la columna grade
como en is_passing_grade
hemos añadido NULL
en el tipo de datos y como valor por defecto. Su finalidad es simplemente aumentar la legibilidad.
Veamos ahora qué condiciones podemos imponer con una restricción CHECK
.
Condiciones de alcance
Podemos asegurarnos de que los valores de una columna están dentro de un rango determinado.
ALTER TABLE table_name
ADD CONSTRAINT constraint_name
CHECK (column_name BETWEEN min_value AND max_value);
Lista de condiciones
Podemos validar que el valor de una columna coincide con uno de una lista de valores específicos.
ALTER TABLE table_name
ADD CONSTRAINT constraint_name
CHECK (column_name IN ('Value1', 'Value2', 'Value3'));
Condiciones de comparación
Podemos comparar los valores de una columna para asegurarnos de que cumplen una condición determinada (mayor que, menor que, etc.).
ALTER TABLE table_name
ADD CONSTRAINT constraint_name
CHECK (column_name > some_value);
Condiciones de concordancia de patrones
Podemos utilizar la concordancia de patrones (por ejemplo, con LIKE
o SIMILAR TO
) para validar datos de texto.
ALTER TABLE table_name
ADD CONSTRAINT constraint_name
CHECK (column_name LIKE 'pattern');
Condiciones lógicas
Podemos permitir condiciones múltiples utilizando operadores lógicos (AND
, OR
, NOT
).
ALTER TABLE table_name
ADD CONSTRAINT constraint_name
CHECK (condition1 AND condition2 OR condition3);
Condiciones compuestas
Aplica una condición sobre varias columnas.
ALTER TABLE table_name
ADD CONSTRAINT constraint_name
CHECK (column1 + column2 < some_value);
FOREIGN KEY Restricción
Las restricciones de clave foránea se utilizan para unir las columnas de dos tablas, garantizando la integridad referencial de los datos. En esencia, una clave externa de una tabla apunta a una clave primaria de otra tabla, indicando que las filas de estas dos tablas están relacionadas. Esto garantiza que no puedas tener una fila en una tabla con clave externa que no se corresponda con ninguna fila de la tabla vinculada con la clave primaria.
En nuestro ejemplo, cada registro de la tabla de inscripciones hace referencia a un alumno y a un curso a través de las columnas student_id
y course_id
, respectivamente. Sin ninguna restricción, no hay nada que garantice que los valores de estos identificadores en la tabla de inscripciones coincidan con las entradas existentes en las tablas de alumnos y cursos.
Así es como nos aseguramos de ello al crear la tabla enrollments
:
CREATE TABLE enrollments (
student_id INT,
course_id INT,
...
FOREIGN KEY (student_id) REFERENCES Students(student_id),
FOREIGN KEY (course_id) REFERENCES Courses(course_id)
);
Ten en cuenta que para que sean claves externas, deben ser claves primarias en las tablas students
y courses
, respectivamente.
Al igual que con otras restricciones, también podríamos declararlas después de la creación de la tabla mediante el comando ALTER TABLE
:
ALTER TABLE enrollments
ADD CONSTRAINT fk_student_id
FOREIGN KEY (student_id)
REFERENCES students(student_id);
ALTER TABLE enrollments
ADD CONSTRAINT fk_course_id
FOREIGN KEY (course_id)
REFERENCES courses(course_id);
Ponerlo todo junto
A lo largo de este artículo, hemos mostrado varias restricciones de integridad y cómo utilizarlas para mejorar una base de datos universitaria. Aquí tienes la versión final del comando CREATE TABLE
, que combina todo lo que hemos aprendido.
En primer lugar, definiremos la tabla students
:
CREATE TABLE students (
student_id INT PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
major TEXT DEFAULT 'Undeclared',
enrollment_year INT,
CONSTRAINT year_check CHECK (enrollment_year >= 1900),
CHECK (major IN (
'Undeclared',
'Computer Science',
'Mathematics',
'Biology',
'Physics',
'Chemistry',
'Biochemistry'
))
);
A continuación, vamos a definir la tabla courses
:
CREATE TABLE courses (
course_id INT PRIMARY KEY,
course_name TEXT NOT NULL,
department TEXT NOT NULL,
UNIQUE (course_name, department),
CHECK (department IN (
'Physics & Mathematics',
'Sciences'
))
);
Por último, definiremos la tabla enrollments
, estableciendo las relaciones entre alumnos y cursos:
CREATE TABLE enrollments (
student_id INT,
course_id INT,
year INT CHECK (year >= 1900),
grade INT NULL DEFAULT NULL,
is_passing_grade BOOLEAN NULL DEFAULT NULL,
CONSTRAINT grade_check CHECK (grade BETWEEN 0 AND 100),
CONSTRAINT is_passing_grade CHECK (
(grade IS NULL AND is_passing_grade IS NULL) OR
(grade >= 60 AND is_passing_grade = TRUE) OR
(grade < 60 AND is_passing_grade = FALSE)
),
FOREIGN KEY (student_id) REFERENCES students(student_id),
FOREIGN KEY (course_id) REFERENCES courses(course_id),
PRIMARY KEY (student_id, course_id, year)
);
Hemos añadido algunas restricciones en este ejemplo final. Una universidad tiene un conjunto conocido de departamentos y especialidades. Por tanto, el conjunto de valores que pueden tener las columnas major
y department
es finito y conocido de antemano. En estas situaciones, se recomienda utilizar una restricción CHECK
para garantizar que las columnas sólo puedan tomar valores de ese conjunto conocido de valores.
Conclusión
En este artículo, hemos explorado los distintos tipos de restricciones de integridad en SQL y cómo implementarlas utilizando PostgreSQL. Cubrimos las claves primarias, las restricciones de NOT NULL
, las restricciones de UNIQUE
, las restricciones de DEFAULT
, las restricciones de CHECK
y las restricciones de FOREIGN KEY
, proporcionando ejemplos prácticos para cada una de ellas.
Comprendiendo estos conceptos, podemos garantizar la exactitud, coherencia y fiabilidad de nuestros datos.
Si quieres aprender más sobre la organización eficaz de los datos, consulta este curso sobre Diseño de Bases de Datos.
Aprende SQL con estos cursos
Course
Unir datos en SQL
Course