====== Snippets MySQL ======
Colección de consultas u operaciones útiles o interesantes de [[informatica:bases_de_datos:mysql|MySQL]].
===== Filtrar con expresiones regulares =====
Buscar registros cuyo campo ''campo1'' comience un numero:
SELECT *
FROM tabla
WHERE campo1 regexp '^[0-9]+'
Buscar registros cuyo campo ''campo1'' comience por alguno de los números indicados:
SELECT *
FROM tabla
WHERE campo1 regexp '^(15001|15002)+'
===== Agrupar =====
==== Agrupar por un valor y concatenar los demás en una columna ====
Por ejemplo, si tenemos:
^ servicio_web ^ email ^
| 11111 | mail@mail.com |
| 22222 | mail@mail.com |
| 33333 | mail@mail.com |
| 44444 | mail@mail.com |
Y queremos obtener el siguiente resultado:
^ email ^ servicio_web ^
| mail@mail.com | 11111, 22222, 33333, 44444 |
Usaremos la función [[https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat|GROUP_CONCAT]]:
SELECT
email,
GROUP_CONCAT(servicio_web ORDER BY servicio_web ASC SEPARATOR ', ')
FROM mitabla
GROUP BY email
==== Búsqueda de valores duplicados ====
Si queremos ver qué valores están duplicados en cierto campo, podemos hacer la siguiente consulta:
SELECT
campo1,
COUNT(*) c
FROM mitabla
GROUP BY campo1
HAVING c > 1
Nos devolverá los valores de ''campo1'' que se repiten (de 1 a más veces)
* [[https://stackoverflow.com/questions/688549/finding-duplicate-values-in-mysql|Finding duplicate values in MySQL]] (Stack Overflow)
===== Fechas =====
==== Conversiones ====
Pasar de marca de tiempo UNIX a formato ISO:
SELECT FROM_UNIXTIME(fecha) as fecha_iso
Salida:
fecha_iso
---------
2020-08-12 00:35:40
El tiempo UNIX (Epoch Time) es el número de segundos transcurridos desde el 01/01/1970 (UTC). Por ejemplo, las 10:00 del 01/08/2020 UTC, serían 1596276000 en tiempo UNIX.
Para convertir una fecha en tiempo UNIX:
SELECT UNIX_TIMESTAMP('2020-08-01 10:00')
Para convertir UTC en cierta zona horaria:
SELECT CONVERT_TZ(fecha_en_utc, 'UTC', 'Europe/Madrid') AS fecha_madrid
FROM tabla
Convertir segundos a formato de tiempo (tipo de dato TIME):
SELECT SEC_TO_TIME(3600)
-- Devolverá '01:00:00'
Convertir un string a tipo fecha:
SELECT str_to_date('12/12/2022 9:04:12', '%d/%m/%Y %k:%i:%s')
Si partimos de un string como ''6/25/2024 2:25:42 AM'', para pasarlo a formato fecha:
SELECT str_to_date('6/25/2024 2:25:42 AM', '%c/%e/%Y %l:%i:%s %p')
* ''%c'': número del mes sin cero inicial (del 1 al 12)
* ''%e'': número del día del mes sin cero inicial (del 1 al 31)
* ''%Y'': número del año
* ''%l'': hora sin cero inicial (de 1 a 12)
* ''%i'': minutos (de 00 a 59)
* ''%s'': segundos (de 00 a 59)
* ''%p'': AM o PM.
[[https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format|Documentación oficial de formateadores de fecha admitidos en MySQL]]
==== Otros ====
Fecha de hoy:
SELECT CURDATE()
También se puede utilizar la función ''CURRENT_DATE''
Fecha de ayer:
SELECT SUBDATE(CURRENT_DATE, 1)
Hace dos semanas:
select DATE_SUB(curdate(), INTERVAL 2 WEEK)
Lunes de la semana actual:
SELECT DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY) as fecha
Lunes de la semana pasada:
SELECT DATE_SUB(DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY), INTERVAL 7 DAY) AS fecha
Agrupar por semanas:
SELECT
fecha,
DATE_FORMAT(fecha, "%x-%v") AS ano_semana
FROM tabla
GROUP BY ano_semana
ORDER BY fecha
* ''%x'': año de la semana donde el lunes es el primer día de la semana. Se usa con ''%v''.
* ''%v'': número de la semana (de 01 a 53) donde el lunes es el primer día de la semana. Se usa con ''%x''.
Si se usa ''WEEK'', hay un problema con la última semana y primera del año. Si no es una semana completa, crea dos registros: una semana con los últimos días del año y otra semana con los primeros días del año siguiente. [[https://en.wikipedia.org/wiki/ISO_8601|Más información sobre la ISO 8601]].
==== Operaciones ====
Sumar/restart tiempo:
-- Añadir 1 día a la fecha
SELECT DATE_ADD(fecha, INTERVAL 1 DAY)
-- Restar 1 día a la fecha
SELECT DATE_SUB(fecha, INTERVAL 1 DAY)
Para restar tiempo de una expresión de tiempo o fecha+tiempo:
SELECT SUBTIME('12:35:00', '1:30:00');
// 11:05:00
* [[https://database.guide/subtime-examples-mysql/|SUBTIME() Examples – MySQL]]
Para ordenar, dejando los valores NULL al final:
SELECT * FROM mitabla
WHERE ...
ORDER BY
CASE
WHEN mifecha IS NULL THEN 1
ELSE 0
END,
mifecha
Diferencia entre fechas:
SELECT TIMEDIFF(fecha_fin, fecha_inicio)
FROM mitabla
-- fecha_fin: 2021-03-30 09:09:40
-- fecha_inicio: 2021-03-30 08:30:01
-- TIMEDIFF = 00:39:39
Diferencia en horas:
SELECT TIMESTAMPDIFF(HOUR, fecha_inicio, fecha_fin)
FROM mitabla
==== Comparaciones ====
Registros de hace más de una hora:
SELECT *
FROM tabla
WHERE fecha_registro < DATE_SUB(NOW(), INTERVAL '1' HOUR)
===== Idioma =====
==== Fechas ====
Para que el nombre del día, mes y sus abreviaturas aparezcan en cierto idioma:
SET lc_time_names = 'es_ES'
SELECT DATE_FORMAT(fecha, '%b') as mes_abreviado
FROM tabla
Salida:
mes_abreviado
-------------
nov
abr
may
jun
dic
El cambio durará hasta que finalice la sesión.
Si queremos ver el valor actual de esa variable:
SELECT @@lc_time_names;
* [[https://dev.mysql.com/doc/refman/8.0/en/locale-support.html|MySQL Server Locale Support]]
===== Extracción de texto =====
==== E-mail ====
Extraer una dirección de e-mail de un campo de texto:
SELECT REGEXP_SUBSTR(campo_texto, '([a-zA-Z0-9._%+\-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,4})') as email
FROM tabla
==== Eliminar saltos de línea ====
Si tenemos algún campo con saltos de línea (''\n'' en sistemas UNIX y ''\r\n'' en Windows), podemos borrarlos con:
UPDATE test SET log = REPLACE(REPLACE(log, '\r', ''), '\n', '');
==== Concatenar campos que pueden ser NULL ====
Si usamos la función ''CONCAT'' y algún campo puede tener valor NULL, la función devolverá NULL. Si este no es el comportamiento que queremos, debemos usar ''CONCAT_WS'' cuyo primer argumento es el separador:
SELECT CONCAT_WS(" ", campo1, campo2, campo3_que_puede_tener_null, campo4)
FROM tabla
CONCAT_WS viene de //Concatenate With Separator//.
* [[https://es.stackoverflow.com/questions/86721/usar-concat-o-concat-ws-en-mysql|¿Usar CONCAT o CONCAT_WS en MySQL?]]
===== Sistema =====
==== Tamaño bases de datos ====
SELECT table_schema "Base de datos",
ROUND(SUM(data_length + index_length) / 1024 / 1024, 1) "Tamaño (MB)"
FROM information_schema.tables
GROUP BY table_schema
===== Usuarios =====
==== Crear usuario ====
CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
==== Dar permisos ====
GRANT ALL PRIVILEGES ON base_datos.tabla TO 'newuser'@'localhost';
FLUSH PRIVILEGES;
==== Mostrar usuarios ====
SELECT user, host FROM mysql.user;
===== Modificaciones =====
==== Último ID insertado ====
Si hacemos una inserción/modificación en una tabla donde una columna tiene el atributo ''AUTO_INCREMENT'', podemos utilizar la función ''mysqli_insert_id()'' para obtener el ID generado por la consulta de inserción/modificación:
(...)
$query = "INSERT INTO myCity VALUES (NULL, 'Stuttgart', 'DEU', 'Stuttgart', 617000)";
mysqli_query($link, $query);
printf ("Nuevo registro con el id %d.\n", mysqli_insert_id($link));
==== Modificar valor inicial AUTO_INCREMENT ====
Si queremos establecer un valor para la clave que contiene AUTO_INCREMENT:
ALTER TABLE mitabla AUTO_INCREMENT=1001;
En las próximas inserciones, la columna con AUTO_INCREMENT se rellenará desde 1001 y siguientes.
==== Insertar varios registros a la vez ====
INSERT INTO nombre_tabla
(a,b,c)
VALUES
(1,2,3),
(4,5,6),
(7,8,9);
===== Tablas =====
==== Duplicar tabla ====
Para duplicar una tabla a partir de otra (conservando índices y estructura) se hace en dos consultas pasos: primero haremos crearemos una tabla a partir de la que nos interesa copiar:
CREATE TABLE tabla_copia LIKE tabla_origen
Y ahora añadimos todo lo que había en la tabla origen a su copia:
INSERT INTO tabla_copia
SELECT *
FROM tabla_origen
==== Modificar una tabla con valores de otra tabla ====
UPDATE tabla_a_actualizar
INNER JOIN tabla_de_la_que_copiar ON (tabla_a_actualizar.valor = tabla_de_la_que_copiar.valor)
SET tabla_a_actualizar.campo1 = tabla_de_la_que_copiar.campo1
* [[https://stackoverflow.com/questions/5727827/update-one-mysql-table-with-values-from-another|Update one MySQL table with values from another]] (Stack Overflow)
==== Listar tablas de una base de datos ====
SELECT table_name
FROM information_schema.tables
WHERE table_schema = ''
==== Eliminar tabla ====
DROP TABLE IF EXISTS
Si queremos eliminar más de una a la vez:
DROP TABLE IF EXISTS , ,
==== Obtener nombre columnas ====
Para ver el listado de campos de una tabla y su tipo:
DESCRIBE
Si queremos obtener el listado de campos separados por coma:
-- Aumentamos la longitud máxima de 'group_concat' (por defecto, 1024 caracteres)
-- Esto solo se mantiene durante la sesión
SET SESSION group_concat_max_len = 1000000;
SELECT group_concat(COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = ''
AND TABLE_NAME = '';
* [[https://stackoverflow.com/a/1526722|Get table column names in MySQL?]]
===== Administración =====
==== Mostrar lista de procesos ====
show full processlist;
Ejemplo de salida:
+---------+-----------+-------------------+-----------------------+---------+------+-------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+---------+-----------+-------------------+-----------------------+---------+------+-------+-----------------------+
| 1195431 | postventa | 10.0.42.185:52702 | tiempos_provision_krt | Sleep | 3559 | | NULL |
| 1195818 | postventa | localhost | NULL | Query | 0 | init | show full processlist |
+---------+-----------+-------------------+-----------------------+---------+------+-------+-----------------------+
==== Detener un proceso ====
Primero buscamos los procesos en ejecución:
SHOW PROCESSLIST;
Y luego, cuando tengamos identificada la ID del proceso que nos interesa, lo detenemos:
KILL
===== Eventos =====
* [[https://codigofacilito.com/articulos/eventos-mysql|Eventos MySQL]]
==== Crear ====
Ejemplo de evento que elimina registros distintos de la fecha actual diariamente a las 00:01:
CREATE EVENT `eliminar_antiguas_tabla_incidencias_hoy`
ON SCHEDULE
EVERY 1 DAY
STARTS (TIMESTAMP(CURRENT_DATE) + INTERVAL 1 DAY + INTERVAL 1 MINUTE)
ON COMPLETION PRESERVE
ENABLE
COMMENT 'Elimina incidencias antiguas de la tabla que guarda incidencias del día en curso'
DO
DELETE FROM qep_tbook.incidencias_hoy WHERE DATE_FORMAT(fecha_creacion, '%Y-%m-%d') <> CURDATE()
==== Mostrar ====
Todos los eventos:
SHOW EVENTS;
==== Deshabilitar ====
ALTER EVENT DISABLE
==== Eliminar ====
DROP EVENT
==== Última ejecución ====
Mostrar los eventos y la última vez que se ejecutaron:
SELECT EVENT_NAME, INTERVAL_VALUE, INTERVAL_FIELD, LAST_EXECUTED
FROM INFORMATION_SCHEMA.events
==== Programador de eventos ====
Activar:
SET GLOBAL event_scheduler = ON;
Desactivar:
SET GLOBAL event_scheduler = OFF;
Consultar estado:
SHOW VARIABLES
WHERE VARIABLE_NAME = 'event_scheduler'