Mostrando entradas con la etiqueta MySQL. Mostrar todas las entradas
Mostrando entradas con la etiqueta MySQL. Mostrar todas las entradas

jueves, 30 de octubre de 2014

Obtener el tamaño de las tablas de una base de datos MySQL

En el anterior post hablábamos sobre cómo obtener el tamaño de las base de datos. En esta ocasión nos centramos en el tamaño de las tablas de una base de datos.

Lo que haremos será una SQL que nos devuelva todas las tablas de la base de datos test ordenadas de la que ocupa más tamaño a la que ocupa menos, expresada la información en MB.

La SQL sería así:

mysql> SELECT  table_name, table_rows,
        ROUND(data_length / (1024 * 1024), 2) AS `data_size`,
        ROUND(index_length / (1024 * 1024), 2) AS `index_size`,
        ROUND((data_length + index_length) / (1024 * 1024), 2) AS `total_size`,
    FROM information_schema.TABLES
    WHERE table_schema = 'test'
    ORDER BY 5 DESC;

Obtener el tamaño de una base de datos MySQL

Hace unos días me pidieron en una aplicación web en la que estaba trabajando que mostrase el tamaño que tenía la base de datos MySQL.

Como no tenía ninguna función a mano se me ocurrió investigar un poco y con esta simple SQL pude obtener la información que solicitaban.


Los datos del tamaño los muestro en MB y en la consulta me devuelve la información de todas las bases de datos a las que tiene acceso el usuario con el que lanzamos la query.

mysql> SELECT table_schema AS `name_bd`, sum( data_length + index_length ) / 1024 / 1024 AS `size`
    ->   FROM information_schema.TABLES
    ->   GROUP BY table_schema;

Lo que devuelve esta función es lo siguiente:

+--------------------+--------------+
| name_bd            | size         |
+--------------------+--------------+
| information_schema |   0.00390625 |
| test               | 173.52998352 |
+--------------------+--------------+
2 rows in set (0.80 sec)

Si quisiéramos obtener únicamente una base de datos en concreto, deberíamos añadir a la consulta una condición WHERE.

mysql> SELECT table_schema AS `name_bd`, sum( data_length + index_length ) / 1024 / 1024 AS `size`
    ->   FROM information_schema.TABLES
    ->   WHERE table_schema='test'
    ->   GROUP BY table_schema;

Y el resultado es:

+----------------+--------------+
| name_bd        | size         |
+----------------+--------------+
| test           | 173.52998352 |
+----------------+--------------+
1 row in set (0.84 sec)

miércoles, 29 de octubre de 2014

Función GROUP_CONCAT de MySQL

En esta ocasión os presento una función muy útil para mostrar datos agrupados cuyo valor de agrupación no es un count() o un sum() o las típicas funciones agregadas. De lo que se trata es de concatenar todos los valores de un campo que cumplan el criterio de un GROUP BY pero separados por el separador que elijamos. Para entenderlo mejor veamos un ejemplo.

Tenemos una tabla llamada ESTUDIANTES con la siguiente estructura:

mysql> select * from estudiantes;
+--------+-------------+------+
| nombre | asignatura  | nota |
+--------+-------------+------+
| María  | Matemáticas |  5.5 |
| María  | Inglés      |    8 |
| María  | Física      | 3.75 |
| Carlos | Matemáticas | 4.25 |
| Carlos | Inglés      |   10 |
| Carlos | Física      |  7.5 |
+--------+-------------+------+
6 rows in set (0.00 sec)

Sobre estos registros queremos extraer por cada estudiante la lista de asignaturas que ha aprobado, es decir, en las que ha sacado más de un 5.

mysql> select nombre, group_concat(asignatura separator '-') from estudiantes where nota>=5 group by nombre;
+--------+----------------------------------------+
| nombre | group_concat(asignatura separator '-') |
+--------+----------------------------------------+
| Carlos | Inglés-Física                          |
| María  | Matemáticas-Inglés                     |
+--------+----------------------------------------+
2 rows in set (0.00 sec)

En este caso hemos indicado que el separador sería un guión pero podríamos haber indicado cualquier otro. Si no indicamos ningún separador nos lo va a devolver separado por comas.

mysql> select nombre, group_concat(asignatura) from estudiantes where nota>=5 group by nombre;
+--------+--------------------------+
| nombre | group_concat(asignatura) |
+--------+--------------------------+
| Carlos | Inglés,Física            |
| María  | Matemáticas,Inglés       |
+--------+--------------------------+
2 rows in set (0.01 sec)

Esto es muy útil y ahora simplemente si recorriésemos los resultados mediante PHP podrías crear fácilmente una tabla HTML

Esta función group_concat() puede devolver como máximo 1024 caracteres que es el límite por defecto tal y como explican en la documentación de MySQL. Sin embargo esto se puede cambiar editando el fichero my.cnf de configuración y modificando el valor de la variable.

group_concat_max_len=16386

Otra forma es ejecutando previamente la siguiente sentencia.

SET GLOBAL group_concat_max_len=4096

Fuentes:

lunes, 20 de octubre de 2014

Tres formas de crear usuarios en MySQL

El sistema gestor de bases de datos MySQL posee una completa gestión y configuración de permisos y usuarios.

En este nuevo post vamos a ver tres posibilidades de creación de usuarios. El primero y el segundo son los más habituales. El tercero ya es algo extraño de utilizar.

1. Sentencia GRANT

La sentencia GRANT proporciona privilegios y permisos a los usuarios de MySQL. Para poder utilizar esta sentencia el usuario con el que accedas a MySQL debe ser el usuario root o un usuario con privilegios de GRANT OPTION.

Si queremos crear un usuario simplemente lanzamos la siguiente sentencia:

mysql> GRANT ALL ON test.* TO 'testuser'@'localhost' IDENTIFIED BY 'pass_testuser';
Query OK, 0 rows affected (0.05 sec)

Con esta sentencia lo que hemos conseguido es darle todos los permisos a un usuario llamado testuser sobre todas las tablas de la base de datos test, que solamente podrá conectarse a la base de datos desde el ámbito localhost y que tiene un password pass_testuser. Además, si el usuario no existe lo crea. Los privilegios ALL [PRIVILEGES] da todos los permisos excepto el GRANT.

La sentencia GRANT, si encuentra que el usuario no existe lo crea, siempre que el servidor MySQL tenga el parámetro NO_AUTO_CREATE_USER inactivo. Si estuviera activo solamente nos permitiría crear usuarios que no tengan password asignado.

La mayor utilidad de este método es que con una única sentencia creamos el usuario y le damos los permisos.

Si se necesita ampliar la información sobre los permisos lo podéis hacer desde la documentación oficial de MySQL sobre GRANT.

2. Sentencia CREATE USER

Con esta sentencia lo que haremos simplemente será crear un usuario, específicándole el ámbito en el que funcionará y su password.

mysql> CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'pass_testuser';
Query OK, 0 rows affected (0.05 sec)

En esta ocasión no le estaremos dando permisos sobre ninguna base de datos. Lo que estamos haciendo es crear el usuario con permisos para conectarse desde localhost y con un password. Si queremos ahora darle permisos podríamos hacer lo siguiente para conseguir lo mismo que en la sentencia GRANT anterior:

mysql> GRANT ALL ON test.* TO 'testuser'@'localhost';
Query OK, 0 rows affected (0.00 sec)

3. Insert directo sobre la tabla user

Este último sería el método más complicado y que MySQL no recomienda. Se trata de realizar un insert sobre la tabla user de la base de datos mysql.

Lo primero que deberíamos conocer es la estructura de la tabla y para ello nos tenemos que conectar a la base de datos mysql:

mysql> use mysql;
Database changed

mysql> describe user;
+-----------------------+-----------------------------------+------+-----+---------+-------+
| Field                 | Type                              | Null | Key | Default | Extra |
+-----------------------+-----------------------------------+------+-----+---------+-------+
| Host                  | char(60)                          | NO   | PRI |         |       |
| User                  | char(16)                          | NO   | PRI |         |       |
| Password              | varchar(41)                       | NO   |     |         |       |
| Select_priv           | enum('N','Y')                     | NO   |     | N       |       |
| Insert_priv           | enum('N','Y')                     | NO   |     | N       |       |
| Update_priv           | enum('N','Y')                     | NO   |     | N       |       |
| Delete_priv           | enum('N','Y')                     | NO   |     | N       |       |
| Create_priv           | enum('N','Y')                     | NO   |     | N       |       |
| Drop_priv             | enum('N','Y')                     | NO   |     | N       |       |
| Reload_priv           | enum('N','Y')                     | NO   |     | N       |       |
| Shutdown_priv         | enum('N','Y')                     | NO   |     | N       |       |
| Process_priv          | enum('N','Y')                     | NO   |     | N       |       |
| File_priv             | enum('N','Y')                     | NO   |     | N       |       |
| Grant_priv            | enum('N','Y')                     | NO   |     | N       |       |
| References_priv       | enum('N','Y')                     | NO   |     | N       |       |
| Index_priv            | enum('N','Y')                     | NO   |     | N       |       |
| Alter_priv            | enum('N','Y')                     | NO   |     | N       |       |
| Show_db_priv          | enum('N','Y')                     | NO   |     | N       |       |
| Super_priv            | enum('N','Y')                     | NO   |     | N       |       |
| Create_tmp_table_priv | enum('N','Y')                     | NO   |     | N       |       |
| Lock_tables_priv      | enum('N','Y')                     | NO   |     | N       |       |
| Execute_priv          | enum('N','Y')                     | NO   |     | N       |       |
| Repl_slave_priv       | enum('N','Y')                     | NO   |     | N       |       |
| Repl_client_priv      | enum('N','Y')                     | NO   |     | N       |       |
| Create_view_priv      | enum('N','Y')                     | NO   |     | N       |       |
| Show_view_priv        | enum('N','Y')                     | NO   |     | N       |       |
| Create_routine_priv   | enum('N','Y')                     | NO   |     | N       |       |
| Alter_routine_priv    | enum('N','Y')                     | NO   |     | N       |       |
| Create_user_priv      | enum('N','Y')                     | NO   |     | N       |       |
| ssl_type              | enum('','ANY','X509','SPECIFIED') | NO   |     |         |       |
| ssl_cipher            | blob                              | NO   |     | NULL    |       |
| x509_issuer           | blob                              | NO   |     | NULL    |       |
| x509_subject          | blob                              | NO   |     | NULL    |       |
| max_questions         | int(11) unsigned                  | NO   |     | 0       |       |
| max_updates           | int(11) unsigned                  | NO   |     | 0       |       |
| max_connections       | int(11) unsigned                  | NO   |     | 0       |       |
| max_user_connections  | int(11) unsigned                  | NO   |     | 0       |       |
+-----------------------+-----------------------------------+------+-----+---------+-------+
37 rows in set (0.00 sec)

Para a continuación poder realizar el insert de la siguiente forma:

mysql> INSERT INTO user VALUES('localhost','testuser',PASSWORD('pass_testuser'),'Y','Y', 'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
Query OK, 1 row affected (0.01 sec)

El último paso que nos falta es refrescar las tablas de permisos de la siguiente forma:

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

Al igual que en el método anterior de CREATE USER, con esto únicamente creamos el usuario, pero no le estamos dando permisos sobre ninguna base de datos. Deberíamos hacerlo igual que en el punto anterior:

mysql> GRANT ALL ON test.* TO 'testuser'@'localhost';
Query OK, 0 rows affected (0.00 sec)

Espero que les pueda servir de ayuda.

Función row_count de MySQL

La función row_count() de MySQL es una función que devuelve el número de filas afectadas de una serie de sentencias SQL tal y como veremos a continuación. Tiene una funcionalidad similar a mysql_affected_rows() de la API de C.

Según el tipo de sentencias devolverá un valor u otro:
  • Sentencias DDL: devuelve un 0. Esto es aplicable a sentencias como CREATE TABLE o DROP TABLE.
  • Sentencias DML diferentes del SELECT: devuelve el número de filas afectadas. Esto se aplica a las sentencias clásicas de INSERT, UPDATE o DELETE, así como a ALTER TABLE LOAD DATA INFILE.
  • Sentencias SELECT: devuelve un -1 si la sentencia devuelve un conjunto de registros. En caso contrario devuelve el número de registros afectados. Por ejemplo SELECT * FROM tabla devuelve -1, pero si hacemos SELECT * FROM tabla INTO OUTFILE 'fichero' devuelve el número de registros que se han volcado al fichero.
  • En las sentencias REPLACE: devuelve un 2 si el nuevo registro reemplaza el anterior. En este caso devuelve un 2 por la fila eliminada y la nueva.

Os presentamos un ejemplo de uso de esta función:

mysql> INSERT INTO tabla VALUES(1),(2),(3);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT ROW_COUNT();
+-------------+
| ROW_COUNT() |
+-------------+
|           3 |
+-------------+
1 row in set (0.00 sec)

mysql> DELETE FROM tabla WHERE i IN(1,2);
Query OK, 2 rows affected (0.00 sec)

mysql> SELECT ROW_COUNT();
+-------------+
| ROW_COUNT() |
+-------------+
|           2 |
+-------------+
1 row in set (0.00 sec)

domingo, 19 de octubre de 2014

Obtener el último registro insertado en una tabla en MySQL

En MySQL tenemos una característica muy común en los campos que hacen de clave primaria que es el AUTO_INCREMENT. Al insertar un registro en una tabla que tenga una clave numérica autoincrementable, es habitual necesitar dicho valor.

Para esto tenemos la función last_insert_id() de MySQL que nos devolverá dicho valor, siempre que estemos en la misma sesión en la que hayamos hecho la inserción.

Veamos un ejemplo claro de su uso con la creación de una tabla vehiculos, otra de conductores y la última que relaciona ambas llamada alquiler_vehiculos.

CREATE TABLE vehiculos(
  id_vehiculo INT NOT NULL AUTO_INCREMENT,
  marca VARCHAR(50) NOT NULL,
  modelo VARCHAR(50) NOT NULL,
  color VARCHAR(50) NOT NULL,
  PRIMARY KEY(id_vehiculo)
);

CREATE TABLE conductores(
  id_conductor INT NOT NULL AUTO_INCREMENT,
  nombre VARCHAR(50) NOT NULL,
  apellidos VARCHAR(50) NOT NULL,
  PRIMARY KEY(id_conductor)
);

CREATE TABLE alquiler_vehiculos(
  id_vehiculo INT NOT NULL,
  id_conductor INT NOT NULL,
  PRIMARY KEY(id_vehiculo, id_conductor)
);


Ahora vamos a insertar registros en la tabla vehiculos. El insert lo podemos hacer de dos formas diferentes obteniendo el mismo resultado. En la primera ponemos un null al campo AUTO_INCREMENT y en la segunda enumeramos los campos omitiendo el primero.

mysql> INSERT INTO vehiculos VALUES (null, 'Ford', 'Focus', 'Negro');
Query OK, 1 row affected (0.28 sec)

mysql> INSERT INTO vehiculos (marca, modelo, color) VALUES ('Ford', 'Focus', 'Negro');
Query OK, 1 row affected (0.28 sec)

En ambos casos podemos recuperar el valor de id_vehiculo con last_insert_id().

mysql> SELECT last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                1 |
+------------------+
1 row in set (0.01 sec)

Ahora lo que haremos será lo mismo para los conductores y en ese momento aprovecharemos el resultado de last_insert_id() para insertar el registro del alquiler.

mysql> INSERT INTO conductores (nombre, apellidos) VALUES ('Carlos', 'Martín');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO alquiler_vehiculos VALUES (1, last_insert_id());
Query OK, 1 row affected (0.23 sec)


Si obtenemos los registros de la tabla veremos que nos ha insertado un 1 en el id_conductor.

mysql> SELECT * FROM alquiler_vehiculos;
+-------------+--------------+
| id_vehiculo | id_conductor |
+-------------+--------------+
|           1 |            1 |
+-------------+--------------+
1 row in set (0.00 sec)

Sin embargo, si hacemos un insert múltiple, la función last_insert_id nos dará el id del primer registro insertado.

mysql> INSERT INTO vehiculos (marca, modelo, color) VALUES ('Ford', 'Focus', 'Blanco'), ('BMW', '330D', 'Gris'), ('Honda', 'Accord', 'Negro');
Query OK, 3 rows affected (0.04 sec)
Registros: 3  Duplicados: 0  Peligros: 0

mysql> SELECT last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)

En este caso, si quisiéramos saber el id del último registro deberíamos combinarlo con la función num_rows() de esta forma.

mysql> SELECT row_count()+last_insert_id()-1;
+--------------------------------+
| row_count()+last_insert_id()-1 |
+--------------------------------+
|                             4 |
+--------------------------------+
1 row in set (0.00 sec)

Espero que os pueda servir de ayuda.

viernes, 26 de septiembre de 2014

Obtener usuarios y permisos MySQL

Los usuarios creados en MySQL se almacenan en una tabla llamada user dentro de la base de datos mysql.
Si queremos obtener el listado de usuarios simplemente podemos lanzar esta consulta:
select user, password, host from mysql.user;

Si después necesitamos obtener la lista de privilegios que tiene uno de los usuarios podemos hacerlo con:
SHOW GRANTS FOR user;

Siendo user uno de los usuarios que nos ha devuelto la SQL anterior que deberemos escribir uniendo el resultado de la columna user y host separados por el símbolo de @. Por ejemplo:
SHOW GRANTS FOR 'root'@'localhost';

domingo, 16 de febrero de 2014

Seleccionar un registro aleatoriamente con MySQL

Si necesitamos obtener una fila aleatoria de una tabla MySQL, es muy probable que si no conocemos sus problemas, optemos por la función RAND().


SELECT * FROM tabla ORDER BY RAND() LIMIT 1;


Éste es una de esas funciones que suenan muy bien a primera vista y donde muchos programadores suelen caer. Sin embargo se trata de una función lenta y que utiliza muchos recursos del servidor ya que MySQL tiene que generar una tabla temporal completa con todos los datos de la tabla original reordenados aleatoriamente.

Si realmente necesitas obtener un registro aleatorio hay mejores formas de hacerlo y así prevenir un cuello de botella que aumentará a medida que aumenta el número de registros de la tabla.

   $res = mysqli->query("SELECT count(*) FROM tabla");
   $fila = $res->fetch_row();
   $rand = mt_rand(0, $fila[0] – 1);
 
   $res = mysqli->query("SELECT * FROM tabla LIMIT $rand, 1");

lunes, 30 de diciembre de 2013

Unir campo DATE y TIME en un único campo TIMESTAMP con MySQL

Hace unos días me encontré la necesidad de unir un campo DATE y TIME para hacer un UPDATE sobre un campo TIMESTAMP de otra tabla.

La solución es muy sencilla. Simplemente tenemos que usar la función TIMESTAMP de la siguiente forma:

UPDATE tabla2, tabla1
SET tabla2.campo=TIMESTAMP(tabla1.campofecha, tabla1.campohora)
WHERE tabla2.id=tabla1.id

Espero que os sirva de ayuda.