jueves, 30 de octubre de 2014

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:

Función sleep, delay, pause o wait en Javascript

Hoy mismo hemos necesitado realizar una función que nos permitiese de forma fácil hacer un sleep, delay, pause o wait mediante Javascript.

Esto es realmente sencillo si usamos un setTimeout() pero no quería tener que llamar a otra función al pasar el tiempo especificado así que he buscado un poco en google y he encontrado esta:

function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }
}


Fuente:

http://www.sitepoint.com/delay-sleep-pause-wait/

martes, 28 de octubre de 2014

Ejecutar proceso en background con PHP

Si necesitamos ejecutar algún proceso en segundo plano en algún script de PHP, podemos llamar a una función sencilla que nos diferenciará si lo estamos ejecutando desde Windows o Linux, ya que el comportamiento es diferente.

El código de esta función es el siguiente:

function execInBackground($cmd) { 
    if (substr(php_uname(), 0, 7) == "Windows"){ 
        pclose(popen("start /B ". $cmd, "r"));  
    } 
    else { 
        exec($cmd . " > /dev/null &");   
    } 
}

Espero que les sirva.

Añadir elementos a un array en PHP

Con PHP tenemos varias formas de añadir elementos a un array. El primero de ellos es la función nativa array_push().

La sintaxis y los parámetros que debemos utilizar son los siguientes:

int array_push ( array &$array , mixed $value1 [, mixed $... ] );

Lo que hacemos es pasarle un array por referencia como primer parámetro y a continuación los elementos a añadir. La función nos devolverá un valor entero con el número de elementos del array después de haber insertado lo que hayamos definido.

Un ejemplo lo podemos ver aquí:

<?php
$myarray = array('pera', 'manzana', 'naranja');
array_push($myarray, 'melón', 'sandía');
?>

Esto devolvería lo siguiente:

Array
(
    [0] => pera
    [1] => manzana
    [2] => naranja
    [3] => melón
    [4] => sandía
)

Otra forma mucho más sencilla para añadir un elemento al array sería:

<?php
$myarray[] = 'melón';
?>

Según la documentación oficial de PHP, deberíamos utilizar esta segunda forma si queremos únicamente añadir un elemento, ya que así evitamos la sobrecarga de llamar a una función. Además, esta segunda forma crea el array si no existe, mientras que con la función array_push() nos devolvería un warning.

Una comparativa sobre la velocidad de ejecución la podemos ver con una simple prueba que detallamos:

<?php
$myarray = array();
for ($i=0; $i<100000; $i++){
    array_push($myarray, $i);
} // tarda 1.5962453267801 segundos

$myarray = array();
for ($i=0; $i<100000; $i++){
    $myarray[] = $i;
} // tarda 0.0614528790371 segundos
?>

En este caso la mejora de rendimiento es del orden de 26 veces.

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.