Los comandos SQL se dividen en tres categorías principales:
DDL (Data Definition Language), lenguaje de definición de datos:
DML (Data Manipulation Language), lenguaje de manipulación de datos:
DCL (Data Control Language), lenguaje de control de datos:
MySQL 8.4CREATE TABLE students ( student_id INT PRIMARY KEY, name VARCHAR(100), age INT );
MySQL 8.4CREATE TABLE enrollments ( enrollment_id INT PRIMARY KEY, student_id INT, course_id INT, FOREIGN KEY (student_id) REFERENCES students(student_id), FOREIGN KEY (course_id) REFERENCES courses(course_id) );
Antes de la normalización:
Después de la normalización:
Tabla Students
Tabla Courses
Tabla Enrollments
Tienes un análisis detallado del tema con ejemplos en el artículo «Normalización de bases de datos: las formas normales explicadas de forma sencilla».
INNER JOIN:
Devuelve los registros que tienen valores coincidentes en ambas tablas.
LEFT JOIN (o LEFT OUTER JOIN):
Devuelve todos los registros de la tabla izquierda y los registros coincidentes de la tabla derecha. Si no hay coincidencia, devuelve NULL para la tabla derecha.
RIGHT JOIN (o RIGHT OUTER JOIN):
Devuelve todos los registros de la tabla derecha y los registros coincidentes de la tabla izquierda. Si no hay coincidencia, devuelve NULL para la tabla izquierda.
FULL OUTER JOIN:
Devuelve todos los registros cuando hay coincidencia en cualquiera de las tablas.
CROSS JOIN:
Realiza el producto cartesiano de dos tablas, combinando cada fila de la primera tabla con cada fila de la segunda.
MySQL 8.4SELECT name FROM employees WHERE department_id = (SELECT id FROM departments WHERE name = 'IT');
MySQL 8.4SELECT sub.department, COUNT(*) FROM ( SELECT department_id AS department FROM employees ) sub GROUP BY sub.department;
Usa la palabra clave DISTINCT en la sentencia SELECT para devolver solo registros únicos.
Ejemplo:
MySQL 8.4SELECT DISTINCT position FROM employees;
Ejemplo:
MySQL 8.4SELECT department_id, COUNT(*) FROM employees WHERE salary > 50000 GROUP BY department_id;
Ejemplo:
MySQL 8.4SELECT department_id, COUNT(*) AS num_employees FROM employees GROUP BY department_id HAVING COUNT(*) > 5;
Los índices aceleran las operaciones de lectura, pero pueden ralentizar las de escritura (inserción, actualización, eliminación), porque hay que actualizar los índices cada vez que cambian los datos.
MySQL 8.4-- Crear un índice sobre la columna name de la tabla employees CREATE INDEX idx_employees_name ON employees(name);
Ejemplo:
MySQL 8.4DELETE FROM employees WHERE salary < 30000;
Ejemplo:
MySQL 8.4TRUNCATE TABLE employees;
Tienes un análisis detallado del tema con ejemplos en el artículo «TRUNCATE, DELETE y DROP: en qué se diferencian y cuál elegir».
Atomicidad (Atomicity):
Consistencia (Consistency):
Aislamiento (Isolation):
Durabilidad (Durability):
Tienes un análisis detallado del tema con ejemplos en el artículo «ACID sin complicaciones: las 4 propiedades de las transacciones».
MySQL 8.4CREATE TRIGGER trg_after_insert_employee AFTER INSERT ON employees FOR EACH ROW BEGIN INSERT INTO audit_log (employee_id, action, action_time) VALUES (NEW.id, 'INSERT', NOW()); END;
MySQL 8.4CREATE VIEW employee_details AS SELECT e.id, e.name, d.name AS department, e.salary FROM employees e JOIN departments d ON e.department_id = d.id;
MySQL 8.4SELECT * FROM employee_details WHERE salary > 50000;
El operador LIKE se usa en la cláusula WHERE para buscar cadenas que coincidan con un patrón determinado. En los patrones se usan caracteres comodín:
MySQL 8.4SELECT * FROM employees WHERE name LIKE 'A%';
MySQL 8.4SELECT * FROM employees WHERE name LIKE '%e';
MySQL 8.4SELECT * FROM employees WHERE name LIKE '_a%';
Las funciones de agregación realizan cálculos sobre un conjunto de valores y devuelven un único valor. A menudo se usan junto con GROUP BY.
MySQL 8.4SELECT COUNT(*) FROM employees;
MySQL 8.4SELECT department_id, AVG(salary) AS average_salary FROM employees GROUP BY department_id;
MySQL 8.4SELECT MAX(salary) FROM employees;
MySQL 8.4SELECT SUM(amount) FROM sales WHERE sale_date BETWEEN '2023-01-01' AND '2023-01-31';
Sintaxis
MySQL 8.4SELECT column_list FROM table1 UNION SELECT column_list FROM table2;
Sintaxis
MySQL 8.4SELECT column_list FROM table1 UNION ALL SELECT column_list FROM table2;
Ejemplo de procedimiento almacenado:
MySQL 8.4CREATE PROCEDURE GetEmployeeByID(IN emp_id INT) BEGIN SELECT * FROM employees WHERE id = emp_id; END;
Llamada al procedimiento:
MySQL 8.4CALL GetEmployeeByID(1);
Ejemplo de función:
MySQL 8.4CREATE FUNCTION GetEmployeeSalary(emp_id INT) RETURNS DECIMAL(10,2) BEGIN DECLARE salary DECIMAL(10,2); SELECT e.salary INTO salary FROM employees e WHERE e.id = emp_id; RETURN salary; END;
Uso de la función:
MySQL 8.4SELECT name, GetEmployeeSalary(id) FROM employees;
Usa índices:
Evita SELECT *:
Optimiza las condiciones de JOIN y WHERE:
Limita los resultados (LIMIT):
Evita subconsultas cuando puedas usar un JOIN:
Cachea los datos que se usan a menudo:
Perfila y analiza las consultas:
Tienes un análisis detallado del tema con ejemplos en el artículo «Optimización de consultas SQL: 10 formas de acelerar una consulta lenta».
Las restricciones garantizan la integridad y la fiabilidad de los datos de una tabla definiendo reglas para los valores de las columnas.
Tipos de restricciones:
NOT NULL:
Ejemplo:
MySQL 8.4CREATE TABLE products ( product_id INT PRIMARY KEY, name VARCHAR(100) NOT NULL );
UNIQUE:
Ejemplo:
MySQL 8.4CREATE TABLE users ( user_id INT PRIMARY KEY, email VARCHAR(100) UNIQUE );
PRIMARY KEY:
FOREIGN KEY:
Ejemplo:
MySQL 8.4CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT, FOREIGN KEY (user_id) REFERENCES users(user_id) );
CHECK:
Ejemplo:
MySQL 8.4CREATE TABLE employees ( id INT PRIMARY KEY, age INT CHECK (age >= 18) );
DEFAULT:
MySQL 8.4CREATE TABLE tasks ( task_id INT PRIMARY KEY, status VARCHAR(20) DEFAULT 'Pending' );
1. Consultas parametrizadas (Prepared Statements):
Ejemplo en Java con JDBC:
MySQL 8.4String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; PreparedStatement stmt = connection.prepareStatement(sql); stmt.setString(1, username); stmt.setString(2, password); ResultSet rs = stmt.executeQuery();
2. Usar un ORM (Object-Relational Mapping):
3. Validar y filtrar los datos de entrada:
4. Restringir los permisos de acceso:
5. Usar procedimientos almacenados:
Ejemplo:
MySQL 8.4SELECT * FROM employees e INNER JOIN departments d ON e.department_id = d.id;
LEFT OUTER JOIN (LEFT JOIN):
Ejemplo:
MySQL 8.4SELECT * FROM employees e LEFT JOIN departments d ON e.department_id = d.id;
RIGHT OUTER JOIN (RIGHT JOIN):
Ejemplo:
MySQL 8.4SELECT * FROM employees e RIGHT JOIN departments d ON e.department_id = d.id;
FULL OUTER JOIN (FULL JOIN):
MySQL 8.4SELECT * FROM employees e FULL OUTER JOIN departments d ON e.department_id = d.id;
Para comprobar si un valor es NULL se usa el operador IS NULL o IS NOT NULL.
MySQL 8.4-- Buscar registros con fecha de nacimiento desconocida SELECT * FROM employees WHERE birth_date IS NULL; -- Buscar registros con fecha de nacimiento conocida SELECT * FROM employees WHERE birth_date IS NOT NULL;
COALESCE
Devuelve el primer elemento de la lista que no sea NULL.
MySQL 8.4COALESCE(val1[, val2, ...., val_n])
ISNULL
Devuelve 1 o 0 según la expresión sea o no NULL.
MySQL 8.4ISNULL(value)
IFNULL
Devuelve el valor pasado como primer argumento si no es NULL. En caso contrario, devuelve el valor pasado como segundo argumento.
MySQL 8.4IFNULL(value, alternative_value)
Tienes un análisis detallado del tema con ejemplos en el artículo «COALESCE en SQL: sustituir NULL y combinarlo con NULLIF».
La expresión CASE se usa para implementar lógica condicional en las consultas SQL. Te permite devolver valores en función de condiciones, de forma parecida a IF-ELSE.
MySQL 8.4CASE WHEN condition1 THEN result1 WHEN condition2 THEN result2 ... ELSE default_result END
MySQL 8.4SELECT name, salary, CASE WHEN salary >= 80000 THEN 'Alto' WHEN salary >= 50000 THEN 'Medio' ELSE 'Bajo' END AS salary_category FROM employees;
MySQL 8.4BEGIN TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE account_id = 1; UPDATE accounts SET balance = balance + 100 WHERE account_id = 2; COMMIT;
MySQL 8.4BEGIN TRANSACTION; DELETE FROM orders WHERE order_date < '2022-01-01'; -- Si cambias de opinión ROLLBACK;
Memoria y rendimiento:
Uso:
Ejemplo:
MySQL 8.4CREATE TABLE products ( code CHAR(10), -- Código de producto de longitud fija name VARCHAR(100) -- Nombre de producto de longitud variable );
Inserción de datos:
MySQL 8.4INSERT INTO products (code, name) VALUES ('A123', 'Portátil Lenovo');
En la columna code, el valor se rellenará con espacios hasta los 10 caracteres.
MySQL 8.4CREATE TEMPORARY TABLE TempTable ( id INT, name VARCHAR(100) );
MySQL 8.4-- Insertar datos en la tabla temporal INSERT INTO #TempTable (id, name) VALUES (1, 'Iván'), (2, 'Pedro'); -- Leer datos de la tabla temporal SELECT * FROM #TempTable; -- La tabla temporal se eliminará automáticamente al terminar la sesión
Tienes una explicación detallada de cómo funcionan las funciones de ventana en nuestro curso.
Tienes un análisis detallado del tema con ejemplos en el artículo «ROW_NUMBER, RANK y DENSE_RANK: las diferencias con un solo ejemplo».
Mejora la legibilidad y la estructura de las consultas complejas.
MySQL 8.4WITH CTEName (column1, column2, ...) AS ( -- Tu consulta SELECT ... ) SELECT * FROM CTEName;
Tienes una explicación detallada de cómo funcionan las CTE en nuestro curso.
Tienes un análisis detallado del tema con ejemplos en el artículo «CTE en SQL: qué es una Common Table Expression, con recursión».
Para eso se usa el comando DROP TABLE, que elimina la tabla y todos sus datos de la base de datos.
MySQL 8.4DROP TABLE table_name;
Garantiza la integridad referencial, asegurando que los valores de la columna de clave foránea coincidan con valores existentes en la tabla relacionada.
Se impide insertar datos incorrectos. No puedes insertar un valor en la columna de clave foránea si ese valor no existe en la tabla relacionada.
Se restringe la eliminación de registros relacionados. No puedes eliminar un registro de la tabla padre si hay registros de la tabla hija que hacen referencia a él, salvo que tomes medidas adicionales.
MySQL 8.4CREATE TABLE Employees ( EmployeeID INT PRIMARY KEY, Name VARCHAR(100), DepartmentID INT, FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID) );
Para eso se usa la sentencia ALTER TABLE con la cláusula ADD, que añade una columna nueva a la tabla.
MySQL 8.4ALTER TABLE table_name ADD column_name data_type [constraints];
Supongamos que tenemos la tabla employees y queremos añadir la columna email de tipo VARCHAR(255).
MySQL 8.4ALTER TABLE employees ADD email VARCHAR(255);
Añadir una columna con la restricción NOT NULL y un valor por defecto:
MySQL 8.4ALTER TABLE employees ADD date_of_birth DATE NOT NULL DEFAULT '1900-01-01';
Particularidad
Si añades una columna con la restricción NOT NULL y la tabla ya contiene datos,
tienes que indicar un valor por defecto; si no, obtendrás un error.
Tenemos las tablas employees y departments.
MySQL 8.4SELECT e.name, e.salary FROM employees e WHERE e.salary > ( SELECT AVG(salary) FROM employees WHERE department_id = e.department_id );
Tienes más información sobre las subconsultas correlacionadas en nuestro curso.
MySQL 8.4DELETE FROM table_name [WHERE condition];
MySQL 8.4TRUNCATE TABLE table_name;
MySQL 8.4DROP TABLE table_name;
Tienes un análisis detallado del tema con ejemplos en el artículo «TRUNCATE, DELETE y DROP: en qué se diferencian y cuál elegir».
Para obtener la lista de empleados con sus responsables:
MySQL 8.4SELECT e.name AS Employee, m.name AS Manager FROM Employee e LEFT JOIN Employee m ON e.managerId = m.employeeId;
En esta consulta combinamos la tabla Employee consigo misma para emparejar a cada empleado con su responsable.
Hacer copias de seguridad de una base de datos y restaurarlas son operaciones críticas para mantener los datos a salvo y poder recuperarlos en caso de fallo o pérdida.
Los métodos de copia de seguridad dependen del sistema de gestión de bases de datos (SGBD) que uses.
A continuación tienes ejemplos para algunos SGBD populares.
Copia de seguridad con la utilidad mysqldump:
MySQL 8.4mysqldump -u username -p mydatabase > backup.sql
Copia de seguridad con la utilidad pg_dump:
MySQL 8.4pg_dump -U username mydatabase > backup.sql
Restaurar la base de datos a partir del archivo backup.sql:
MySQL 8.4mysql -u username -p mydatabase < backup.sql
Restaurar la base de datos con la utilidad psql:
MySQL 8.4psql -U username mydatabase < backup.sql
Las relaciones de muchos a muchos en las bases de datos relacionales aparecen cuando un registro de la primera tabla puede corresponder a varios registros de la segunda, y viceversa.
En SQL, estas relaciones se implementan con una tabla intermedia (también llamada tabla de relación o tabla de unión), que conecta las dos tablas principales mediante claves foráneas.
Imagina un escenario con las tablas Student (estudiantes) y Course (cursos), en el que un estudiante puede matricularse en varios cursos y un curso puede tener varios estudiantes.
MySQL 8.4CREATE TABLE Student ( StudentID INT PRIMARY KEY, Name VARCHAR(100) );
MySQL 8.4CREATE TABLE Course ( CourseID INT PRIMARY KEY, Title VARCHAR(100) );
MySQL 8.4CREATE TABLE StudentCourse ( StudentID INT, CourseID INT, PRIMARY KEY (StudentID, CourseID), FOREIGN KEY (StudentID) REFERENCES Student(StudentID), FOREIGN KEY (CourseID) REFERENCES Course(CourseID) );
Resultado:
Los comandos GRANT y REVOKE en SQL se usan para gestionar los permisos de acceso de los usuarios a los objetos de la base de datos. Te permiten conceder o revocar privilegios concretos a usuarios o roles, lo que garantiza la seguridad y el control sobre quién puede hacer qué en la base de datos.
El comando GRANT concede a usuarios o roles determinados privilegios sobre los objetos de la base de datos.
MySQL 8.4GRANT privileges ON object TO user [WITH GRANT OPTION];
Ejemplo:
Así puedes conceder al usuario user1 permiso para seleccionar datos de la tabla employees:
MySQL 8.4GRANT SELECT ON employees TO user1;
El comando REVOKE retira a usuarios o roles los privilegios concedidos anteriormente.
MySQL 8.4REVOKE privileges ON object FROM user;
Ejemplo:
Retirar al usuario user1 el permiso para seleccionar datos de la tabla employees:
MySQL 8.4REVOKE SELECT ON employees FROM user1;
Las funciones hash en SQL se usan para convertir datos de entrada de longitud arbitraria en una cadena de longitud fija. Esta conversión se llama hashing y se usa mucho para garantizar la seguridad y la integridad de los datos y para optimizar las operaciones de comparación y búsqueda.
Las funciones hash permiten saber si los datos se han modificado. Si calculas el hash de los datos originales y lo comparas con el hash actual, puedes detectar los cambios.
Los valores hash pueden usarse para comparar rápidamente grandes volúmenes de datos o para crear índices que aceleren las búsquedas.
El hashing ayuda a generar identificadores únicos para los registros a partir de su contenido.
MySQL 8.4-- Creamos la tabla de usuarios CREATE TABLE Users ( UserID INT PRIMARY KEY, Username VARCHAR(50) NOT NULL, PasswordHash VARBINARY(512) NOT NULL ); -- Al añadir un usuario nuevo, calculamos el hash de su contraseña INSERT INTO Users (UserID, Username, PasswordHash) VALUES (1, 'user1', HASHBYTES('SHA2_512', 'password123')); -- Comprobamos si la contraseña introducida coincide con el hash de la base de datos SELECT UserID FROM Users WHERE Username = 'user1' AND PasswordHash = HASHBYTES('SHA2_512', 'password123');
Guarda los hashes en formato binario (VARBINARY) en lugar de como cadena (VARCHAR) para conservar la precisión y ahorrar espacio.
Recuerda que el hashing es una función unidireccional: no es posible recuperar los datos originales a partir del hash.
El trigger se activa automáticamente en respuesta a un evento definido (por ejemplo, cuando se añade un registro nuevo a una tabla).
Dentro del trigger puedes acceder a los valores antiguos y nuevos de los datos mediante las pseudotablas especiales OLD y NEW.
Tenemos la tabla Employees y queremos guardar automáticamente el historial de cambios de salario de los empleados en la tabla SalaryHistory cada vez que se actualizan los datos.
Creación de las tablas:
MySQL 8.4-- Tabla de empleados CREATE TABLE Employees ( EmployeeID INT PRIMARY KEY, Name VARCHAR(100), Salary DECIMAL(10, 2) ); -- Tabla del historial de salarios CREATE TABLE SalaryHistory ( EmployeeID INT, OldSalary DECIMAL(10, 2), NewSalary DECIMAL(10, 2), ChangeDate DATETIME DEFAULT CURRENT_TIMESTAMP );
Creación del trigger:
MySQL 8.4DELIMITER $$ CREATE TRIGGER trg_AfterSalaryUpdate AFTER UPDATE ON Employees FOR EACH ROW BEGIN IF OLD.Salary <> NEW.Salary THEN INSERT INTO SalaryHistory (EmployeeID, OldSalary, NewSalary) VALUES (NEW.EmployeeID, OLD.Salary, NEW.Salary); END IF; END$$ DELIMITER ;