3. Manipulación de Datos (DML)
Manipulación de Datos (DML – Data Manipulation Language)
Section titled “Manipulación de Datos (DML – Data Manipulation Language)”El Lenguaje de Manipulación de Datos (DML) es un subconjunto de SQL que permite a los usuarios insertar, consultar, actualizar y eliminar datos en una base de datos. Estas operaciones son fundamentales para interactuar con los datos almacenados en las tablas.
3.1. Insertar registros (INSERT)
Section titled “3.1. Insertar registros (INSERT)”El comando INSERT permite añadir nuevos registros a una tabla existente.
Sintaxis básica
Section titled “Sintaxis básica”INSERT INTO nombre_tabla [(columna1, columna2, ...)] VALUES (valor1, valor2, ...);Ejemplos de inserción de datos
Section titled “Ejemplos de inserción de datos”-- Insertar un registro especificando todas las columnasINSERT INTO empleados (id_empleado, nombre, apellido, email, fecha_contratacion, salario)VALUES (1, 'Juan', 'Pérez', 'juan.perez@ejemplo.com', '2023-01-15', 35000.00);
-- Insertar un registro sin especificar columnas (debe seguir el orden de las columnas en la tabla)INSERT INTO departamentosVALUES (1, 'Ventas', 'Edificio A', 101);-- Insertar múltiples registros en una sola sentenciaINSERT INTO productos (id_producto, nombre, precio, stock) VALUES(101, 'Teclado inalámbrico', 45.99, 50),(102, 'Mouse óptico', 25.50, 100),(103, 'Monitor 24"', 199.99, 25);-- Insertar datos desde otra tablaINSERT INTO empleados_historico (id_empleado, nombre, apellido, fecha_baja)SELECT id_empleado, nombre, apellido, CURRENT_DATEFROM empleadosWHERE departamento_id = 5;Diferencias entre MySQL y Oracle
Section titled “Diferencias entre MySQL y Oracle”-- Insertar ignorando duplicadosINSERT IGNORE INTO clientes (id_cliente, nombre, email)VALUES (1, 'Ana García', 'ana@ejemplo.com');
-- Insertar o actualizar si existe (UPSERT)INSERT INTO productos (id_producto, nombre, precio, stock)VALUES (101, 'Teclado mecánico', 89.99, 30)ON DUPLICATE KEY UPDATEprecio = VALUES(precio),stock = stock + VALUES(stock);-- Insertar con secuencia para generar IDINSERT INTO empleados (id_empleado, nombre, apellido)VALUES (empleados_seq.NEXTVAL, 'Carlos', 'Ruiz');
-- Insertar o actualizar si existe (MERGE)MERGE INTO productos pUSING (SELECT 101 AS id, 'Teclado mecánico' AS nombre, 89.99 AS precio, 30 AS stock FROM dual) dON (p.id_producto = d.id)WHEN MATCHED THEN UPDATE SET p.precio = d.precio, p.stock = p.stock + d.stockWHEN NOT MATCHED THEN INSERT (id_producto, nombre, precio, stock) VALUES (d.id, d.nombre, d.precio, d.stock);3.2. Consultar datos (SELECT)
Section titled “3.2. Consultar datos (SELECT)”El comando SELECT es uno de los más utilizados en SQL y permite recuperar datos de una o más tablas.
Sintaxis básica
Section titled “Sintaxis básica”SELECT [DISTINCT] columna1, columna2, ...FROM nombre_tabla[WHERE condicion][GROUP BY columnas][HAVING condicion_grupo][ORDER BY columnas [ASC|DESC]][LIMIT n];Ejemplos de consultas básicas
Section titled “Ejemplos de consultas básicas”-- Seleccionar todas las columnas de una tablaSELECT * FROM empleados;
-- Seleccionar columnas específicasSELECT nombre, apellido, salario FROM empleados;
-- Seleccionar con alias de columnaSELECT nombre AS nombre_empleado, salario AS salario_mensual, salario * 12 AS salario_anualFROM empleados;Eliminar duplicados con DISTINCT
Section titled “Eliminar duplicados con DISTINCT”-- Obtener departamentos únicos donde trabajan empleadosSELECT DISTINCT departamento_id FROM empleados;
-- Combinaciones únicas de departamento y cargoSELECT DISTINCT departamento_id, cargo FROM empleados;Funciones de agregación
Section titled “Funciones de agregación”-- Contar registrosSELECT COUNT(*) AS total_empleados FROM empleados;
-- Calcular suma, promedio, máximo y mínimoSELECT SUM(salario) AS suma_salarios, AVG(salario) AS promedio_salario, MAX(salario) AS salario_maximo, MIN(salario) AS salario_minimoFROM empleados;Agrupación con GROUP BY
Section titled “Agrupación con GROUP BY”-- Contar empleados por departamentoSELECT departamento_id, COUNT(*) AS num_empleadosFROM empleadosGROUP BY departamento_id;
-- Calcular salario promedio por departamento y cargoSELECT departamento_id, cargo, AVG(salario) AS salario_promedioFROM empleadosGROUP BY departamento_id, cargo;Filtrar grupos con HAVING
Section titled “Filtrar grupos con HAVING”-- Encontrar departamentos con más de 5 empleadosSELECT departamento_id, COUNT(*) AS num_empleadosFROM empleadosGROUP BY departamento_idHAVING COUNT(*) > 5;3.3. Filtrar resultados (WHERE)
Section titled “3.3. Filtrar resultados (WHERE)”La cláusula WHERE permite filtrar los registros que cumplen con una condición específica.
Operadores de comparación
Section titled “Operadores de comparación”-- IgualdadSELECT * FROM empleados WHERE departamento_id = 3;
-- DesigualdadSELECT * FROM empleados WHERE salario > 50000;SELECT * FROM productos WHERE categoria_id != 5;
-- ComparacionesSELECT * FROM pedidos WHERE fecha_pedido >= '2023-01-01';SELECT * FROM productos WHERE stock <= 10;Operadores lógicos
Section titled “Operadores lógicos”-- AND: ambas condiciones deben ser verdaderasSELECT * FROM empleadosWHERE departamento_id = 3 AND salario > 40000;
-- OR: al menos una condición debe ser verdaderaSELECT * FROM productosWHERE categoria_id = 1 OR categoria_id = 2;
-- NOT: niega una condiciónSELECT * FROM clientesWHERE NOT ciudad = 'Madrid';Operadores especiales
Section titled “Operadores especiales”BETWEEN
Section titled “BETWEEN”-- Valores dentro de un rango (inclusivo)SELECT * FROM productosWHERE precio BETWEEN 10.00 AND 50.00;
-- Fechas dentro de un rangoSELECT * FROM pedidosWHERE fecha_pedido BETWEEN '2023-01-01' AND '2023-03-31';-- Valores que coinciden con cualquiera en una listaSELECT * FROM empleadosWHERE departamento_id IN (1, 3, 5);
-- Subconsulta con INSELECT * FROM productosWHERE categoria_id IN (SELECT id_categoria FROM categorias WHERE activo = 1);-- Coincidencia de patrones con comodín %SELECT * FROM empleados WHERE nombre LIKE 'Mar%'; -- Nombres que empiezan con "Mar"SELECT * FROM productos WHERE descripcion LIKE '%portátil%'; -- Contiene "portátil"SELECT * FROM clientes WHERE email LIKE '%@gmail.com'; -- Termina con "@gmail.com"
-- Comodín _ (un solo carácter)SELECT * FROM empleados WHERE codigo LIKE 'E-___'; -- E- seguido de exactamente 3 caracteresIS NULL / IS NOT NULL
Section titled “IS NULL / IS NOT NULL”-- Encontrar valores nulosSELECT * FROM empleados WHERE fecha_baja IS NULL;
-- Encontrar valores no nulosSELECT * FROM clientes WHERE telefono IS NOT NULL;-- Búsqueda de texto completo (requiere índice FULLTEXT)SELECT * FROM articulosWHERE MATCH(titulo, contenido) AGAINST('inteligencia artificial');
-- Expresiones regularesSELECT * FROM productosWHERE nombre REGEXP '^[A-Z][0-9]';-- Expresiones regularesSELECT * FROM productosWHERE REGEXP_LIKE(nombre, '^[A-Z][0-9]');
-- Búsqueda de texto con Oracle TextSELECT * FROM articulosWHERE CONTAINS(contenido, 'inteligencia NEAR artificial') > 0;3.4. Ordenar resultados (ORDER BY)
Section titled “3.4. Ordenar resultados (ORDER BY)”La cláusula ORDER BY permite ordenar los resultados de una consulta según una o más columnas.
Sintaxis básica
Section titled “Sintaxis básica”SELECT columna1, columna2, ...FROM nombre_tabla[WHERE condicion]ORDER BY columna1 [ASC|DESC], columna2 [ASC|DESC], ...;Ejemplos de ordenación
Section titled “Ejemplos de ordenación”-- Ordenar por una columna (ascendente por defecto)SELECT nombre, apellido, salario FROM empleadosORDER BY salario;
-- Ordenar de forma descendenteSELECT nombre, apellido, salario FROM empleadosORDER BY salario DESC;
-- Ordenar por múltiples columnasSELECT nombre, apellido, departamento_id, salario FROM empleadosORDER BY departamento_id ASC, salario DESC;Ordenar por posición de columna
Section titled “Ordenar por posición de columna”-- Ordenar por la segunda columna seleccionadaSELECT nombre, fecha_contratacion, salario FROM empleadosORDER BY 2 DESC; -- Ordena por fecha_contratacion descendenteOrdenar con expresiones
Section titled “Ordenar con expresiones”-- Ordenar por una expresión calculadaSELECT nombre, apellido, salario, salario * 1.1 AS salario_con_aumentoFROM empleadosORDER BY salario * 1.1 DESC;
-- También se puede ordenar por el aliasSELECT nombre, apellido, salario, salario * 1.1 AS salario_con_aumentoFROM empleadosORDER BY salario_con_aumento DESC;Ordenar con valores NULL
Section titled “Ordenar con valores NULL”-- En MySQL, NULL se considera menor que cualquier valor no NULL-- Por defecto, los NULL aparecen primero en orden ASC
-- Colocar NULL al final en ordenación ascendenteSELECT nombre, fecha_baja FROM empleadosORDER BY fecha_baja IS NULL, fecha_baja ASC;-- En Oracle, se puede especificar explícitamente dónde colocar los NULL
-- NULL primero (comportamiento por defecto en ASC)SELECT nombre, fecha_baja FROM empleadosORDER BY fecha_baja ASC NULLS FIRST;
-- NULL últimoSELECT nombre, fecha_baja FROM empleadosORDER BY fecha_baja ASC NULLS LAST;3.5. Limitar resultados (LIMIT en MySQL, ROWNUM en Oracle)
Section titled “3.5. Limitar resultados (LIMIT en MySQL, ROWNUM en Oracle)”A menudo es útil limitar el número de filas devueltas por una consulta, especialmente cuando se trabaja con grandes conjuntos de datos.
LIMIT en MySQL
Section titled “LIMIT en MySQL”-- Obtener los primeros 10 registrosSELECT * FROM productosORDER BY precio DESCLIMIT 10;
-- Paginación: obtener registros del 11 al 20-- LIMIT [offset], [count]SELECT * FROM productosORDER BY precio DESCLIMIT 10, 10;
-- Sintaxis alternativa para paginaciónSELECT * FROM productosORDER BY precio DESCLIMIT 10 OFFSET 10;ROWNUM y ROW_NUMBER() en Oracle
Section titled “ROWNUM y ROW_NUMBER() en Oracle”-- Usando ROWNUM (Oracle 11g y anteriores)-- IMPORTANTE: ROWNUM se aplica antes de ORDER BY, por lo que debe usarse con subconsultasSELECT * FROM ( SELECT * FROM productos ORDER BY precio DESC) WHERE ROWNUM <= 10;
-- Para paginación en Oracle 12c y posterioresSELECT * FROM productosORDER BY precio DESCFETCH FIRST 10 ROWS ONLY;
-- Paginación: obtener registros del 11 al 20SELECT * FROM productosORDER BY precio DESCOFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
-- Usando ROW_NUMBER() (Oracle 11g y posteriores)SELECT * FROM ( SELECT p.*, ROW_NUMBER() OVER (ORDER BY precio DESC) AS rn FROM productos p) WHERE rn BETWEEN 11 AND 20;3.6. Actualizar registros (UPDATE)
Section titled “3.6. Actualizar registros (UPDATE)”El comando UPDATE permite modificar datos existentes en una tabla.
Sintaxis básica
Section titled “Sintaxis básica”UPDATE nombre_tablaSET columna1 = valor1, columna2 = valor2, ...[WHERE condicion];Ejemplos de actualización
Section titled “Ejemplos de actualización”-- Actualizar un solo registroUPDATE empleadosSET salario = 55000WHERE id_empleado = 101;
-- Actualizar múltiples columnasUPDATE clientesSET telefono = '555-123-4567', direccion = 'Calle Nueva 123', fecha_actualizacion = CURRENT_DATEWHERE id_cliente = 25;
-- Actualizar múltiples registrosUPDATE productosSET precio = precio * 1.1WHERE categoria_id = 3;Actualizaciones con subconsultas
Section titled “Actualizaciones con subconsultas”-- Actualizar basado en datos de otra tablaUPDATE productos pSET p.precio = p.precio * 1.2WHERE p.categoria_id IN ( SELECT id_categoria FROM categorias WHERE nombre = 'Electrónica');-- Actualizar con JOIN en MySQLUPDATE productos pJOIN categorias c ON p.categoria_id = c.id_categoriaSET p.precio = p.precio * 1.2WHERE c.nombre = 'Electrónica';
-- Actualizar múltiples tablasUPDATE pedidos p, detalles_pedido dpSET p.estado = 'Completado', dp.estado_linea = 'Enviado'WHERE p.id_pedido = dp.id_pedidoAND p.id_pedido = 1001;-- Actualizar con subconsulta correlacionadaUPDATE productos pSET p.precio = p.precio * 1.2WHERE EXISTS ( SELECT 1 FROM categorias c WHERE p.categoria_id = c.id_categoria AND c.nombre = 'Electrónica');
-- Actualizar con MERGEMERGE INTO productos pUSING categorias cON (p.categoria_id = c.id_categoria)WHEN MATCHED THEN UPDATE SET p.precio = p.precio * 1.2 WHERE c.nombre = 'Electrónica';3.7. Eliminar registros (DELETE)
Section titled “3.7. Eliminar registros (DELETE)”El comando DELETE permite eliminar registros existentes de una tabla.
Sintaxis básica
Section titled “Sintaxis básica”DELETE FROM nombre_tabla[WHERE condicion];Ejemplos de eliminación
Section titled “Ejemplos de eliminación”-- Eliminar un registro específicoDELETE FROM clientesWHERE id_cliente = 1001;
-- Eliminar múltiples registrosDELETE FROM productosWHERE categoria_id = 5 AND stock = 0;
-- Eliminar registros basados en una fechaDELETE FROM registros_logWHERE fecha_registro < '2023-01-01';Eliminar con subconsultas
Section titled “Eliminar con subconsultas”-- Eliminar registros basados en datos de otra tablaDELETE FROM pedidosWHERE cliente_id IN ( SELECT id_cliente FROM clientes WHERE fecha_ultima_compra < '2020-01-01');-- Eliminar con JOINDELETE pFROM pedidos pJOIN clientes c ON p.cliente_id = c.id_clienteWHERE c.fecha_ultima_compra < '2020-01-01';
-- Eliminar con límiteDELETE FROM registros_logORDER BY fecha_registroLIMIT 1000;-- Eliminar con subconsulta correlacionadaDELETE FROM pedidos pWHERE EXISTS ( SELECT 1 FROM clientes c WHERE p.cliente_id = c.id_cliente AND c.fecha_ultima_compra < TO_DATE('2020-01-01', 'YYYY-MM-DD'));
-- Eliminar con ROWNUM (similar a LIMIT)DELETE FROM ( SELECT * FROM registros_log ORDER BY fecha_registro)WHERE ROWNUM <= 1000;Diferencias entre DELETE y TRUNCATE
Section titled “Diferencias entre DELETE y TRUNCATE”- Es una operación DML (Data Manipulation Language)
- Puede usar cláusula WHERE para eliminar registros específicos
- Registra cada eliminación en el log de transacciones
- Se puede hacer rollback
- Activa disparadores (triggers)
- No reinicia contadores de secuencia/autoincremento
- Más lento para eliminar todos los registros
- Es una operación DDL (Data Definition Language)
- Elimina todos los registros (no permite WHERE)
- Mínima escritura en el log de transacciones
- No se puede hacer rollback (en la mayoría de SGBD)
- No activa disparadores
- Reinicia contadores de secuencia/autoincremento
- Más rápido para eliminar todos los registros
Eliminación lógica vs. física
Section titled “Eliminación lógica vs. física”En muchas aplicaciones empresariales, es común implementar una “eliminación lógica” en lugar de eliminar físicamente los registros:
-- Estructura de tabla con columna para eliminación lógicaCREATE TABLE clientes ( id_cliente INT PRIMARY KEY, nombre VARCHAR(100), activo BOOLEAN DEFAULT TRUE, -- o bit, tinyint, etc. fecha_eliminacion DATETIME NULL);
-- "Eliminar" lógicamente un clienteUPDATE clientesSET activo = FALSE, fecha_eliminacion = CURRENT_TIMESTAMPWHERE id_cliente = 1001;
-- Consultar solo clientes activosSELECT * FROM clientes WHERE activo = TRUE;