lunes, 14 de marzo de 2011

Obteniendo comentarios de tablas y columnas - diccionario de datos

Se presentó el típico problema de falta de documentación! Entender un sistema es complejo y paradojicamente, pareciera que cada vez más... Muchas tecnologías, muchos entornos de trabajo, muchas maneras de configurarlos, mucho... Spring, GWT, Hibernate, Symfony, CakePHP, Rails, Struts, etc, etc, etc, etc... Y etc...

Como para rematar, más que un sistema sin documentación, una BD sin ella, es como buscar una aguja en un pajar... Como mínimo deberíamos tener un diccionario de datos y alguno de esos diagramitas (ER - Peter Chen) generados con alguna herramienta CASE.

El problema que persigue a cualquier "UP frutado" y que además haya leído algo con aires del Manifiesto por el Desarrollo Ágil de Software es: si debe generar ese diccionario en un documento que seguramente no será actualizado más nunca en la vida y del que no podrá generarse un artefacto de software.

Idealmente, en un modelo bottom-up, podemos comenzar con un CASE e ir incluyendo los comentarios según realizamos el modelo y al generar nuestro script SQL, pum! Ya tenemos comentarios en BD.

Ahora, todo esto es bello como la pradera, pero mentira como el comercial de Coca-Cola (el de la canción de Oasis, Whatever). Esto seguramente no se actualizará más nunca en la vida.

Con base en todo lo que he mencionado, decidí incluir los comentarios en BD directamente. Y cuando deseo leerlos, pues lo hago con el MySQL Admin.

Ahora, qué pasa si el jefe o alguien no técnico quiere esa info... O simplemente quieres tenerla en otro formato... Precisamente ese fue uno de los problemas con los que me conseguí y realmente MySQL no me daba una herramienta que se adaptara a lo que necesitaba... Les dejo este sencillo script que pretende facilitarles la vida... Por supuesto, se pueden incluir muchas más cosas en la salida:

#!/bin/bash

if [ $# -lt 3 ]; then
        echo "You must specify table, schema and host"
        echo "Eg. script.sh TABLE SCHEMA 192.168.10.1"
        exit
fi

table="$1"
schema="$2"
host="$3"
user="root"

stty -echo
read -p "Enter MySQL's Admin password: " password
stty echo
echo

mysql -u $user --password=$password -h $host --batch information_schema --skip-column-names -e "SELECT table_name, table_comment FROM tables WHERE table_name='$table' AND table_schema='$schema'" | sed 's/\t/|/g'

mysql -u $user --password=$password -h $host --batch information_schema --skip-column-names -e "SELECT column_name, column_type, column_comment FROM columns WHERE table_name='$table' AND table_schema='$schema'" | sed 's/\t/|/g'

El script de arriba imprime campos separados por tuberías "|". Específicamente, nombre de tabla, nombre de columnas, tipos y comentarios. Un ejemplo de salida:

$ ./extractComments.sh mia test localhost 
Enter MySQL's Admin password: 
mia|Descripcion de la tabla
id|int(11)|Descripcion del campo id
name|varchar(50)|

Y puede utilizar esta variante para extraer el "Diccionario de Datos" de toda la BD:

#!/bin/bash

if [ $# -lt 2 ]; then
        echo "You must specify table, schema and host"
        echo "Eg. script.sh SCHEMA 192.168.10.1"
        exit
fi

schema="$1"
host="$2"
user="Admin"

stty -echo
read -p "Enter MySQL's Admin password: " password
stty echo
echo

mysql -u $user --password=$password -h $host --batch $schema --skip-column-names -e "SHOW TABLES" |
while read table; do

  mysql -u $user --password=$password -h $host --batch information_schema --skip-column-names -e "SELECT table_name, table_comment FROM tables WHERE table_name='$table' AND table_schema='$schema'" | sed 's/\t/|/g'

  mysql -u $user --password=$password -h $host --batch information_schema --skip-column-names -e "SELECT column_name, column_type, column_comment FROM columns WHERE table_name='$table' AND table_schema='$schema'" | sed 's/\t/|/g'

  echo; echo
done;


viernes, 11 de marzo de 2011

Barajita premiada (#22) - copiando en consola con barra de progreso

Copiar ficheros de un lado a otro por consola resulta trivial, esto se puede hacer utilizando el comando cp. Luego, hay momentos en los que se debe copiar mucha información... Por ejemplo, yo hago respaldos de mi info en un disco externo cada cierto tiempo y son varios gigas, entonces al hacer una copia no conocía nada del estatus de la copia, sólo me quedaba la persignación... XD

Podría pensar que soy un freak por hacer copias por consola, la cuestión es que es mucho más útil porque puedo manipular la salida de error estándar y no dejo colgada la interfaz gráfica!

Generalmente hacía algo como:

# cp -r /etc/ /home/ /opt/ /root/ /var/ 201111_respaldo_linux 2>201111_respaldo_linux/errores

Ahora, sólo sabía como iba esto cuando terminaba... Como máximo podía ir viendo errores con un "tail -f" sobre el fichero de errores, algo así:

# cp -r /etc/ /home/ /opt/ /root/ /var/ 201111_respaldo_linux 2>201111_respaldo_linux/errores & tail -f 201111_respaldo_linux/errores

Pero ya no sufro más!!! Encontré una mejor manera de hacer esto, utilizando rsync que me da detalle del progreso de la operación...

# rsync -aRv --progress --delete /etc/ /home/ /opt/ /root/ /var/ 201111_respaldo_linux 2>201111_respaldo_linux/errores

Esto mostrará el estatus de la copia (al más puro estilo scp), las opciones: -a, -R, -v, --progress y --delete, las utilizo -respectivamente- para: copiar ficheros, utilizar nombres relativos, activar modo verboso, mostrar progreso y eliminar contenidos diferentes de la fuente en el destino.


lunes, 28 de febrero de 2011

Optimizando tablas InnoDB en MySQL

Cuando utilizamos MySQL es común optimizar tablas con muchos registros con cierta periodicidad, esto para solventar problemas de fragmentación, entre otros. La verdad esta es una de las cosas del modelo de PostgreSQL que echo en falta, quizás no es tan "amigable" pero todo queda claro desde el inicio.

En PostgreSQL hay un proceso de aspiradora (vacuum) que va eliminando periódicamente registros inutilizados en tablas, su configuración, pan nuestro de cada día para un admin de BBDD que debe ajustarlo con frecuencia.

Bueno.... Volviendo a MySQL, si necesita optimizar tablas InnoDB, lo mejor que puede utilizar son ALTER nulos, estas son instrucciones DDL de tipo ALTER sin parámetros que permiten seguir trabajando con las BBDD, porque realiza copias temporales en disco. La cuestión es que esta herramienta "reconstruye" la tabla y elimina, entre otros, los problemas de fragmentación.

Aquí les dejo un script para optimizar de "un sólo golpe" varias tablas InnoDB:

#!/bin/bash

if [ $# -lt 2 ]; then
        echo "You must specify database host"
        echo "Eg. script.sh MY_DATABSE 192.168.10.1"
        exit
fi

db="$1"
host="$2"
user="root"
declare -a tables=(Table1 Table2 Table3)

stty -echo
read -p "Enter MySQL's Admin password: " password
stty echo

for table in ${tables[@]}; do
        echo $table &&
        time mysql -u $user --password=$password -h $host $db -e "ALTER TABLE $table ENGINE=INNODB"
done

Básicamente optimizamos las tablas especificadas (en un arreglo) e imprimimos el tiempo que toma cada instrucción (time).

Si tiene la certeza de que todas las tablas de una BD son InnoDB y quiere optimizarlas todas aún más rápido, puede hacerlo valiéndose del comando "show tables"...

#!/bin/bash

if [ $# -lt 2 ]; then
        echo "You must specify database host"
        echo "Eg. script.sh MY_DATABSE 192.168.10.1"
        exit
fi

db="$1"
host="$2"
user="root"

stty -echo
read -p "Enter MySQL's Admin password: " password
stty echo

mysql -u $user --password=$password -h $host --batch --skip-column-names $db -e "SHOW TABLES" |
while read table; do
        echo $table &&
        time mysql -u $user --password=$password -h $host $db -e "ALTER TABLE $table ENGINE=INNODB"
done

La única diferencia es que las tablas ya no son especificadas a través de un arreglo (que recomiendo para BBDD grandes, donde optimizar todas las tablas podría demorar toda la vida), sino que se toman directamente del comando "SHOW TABLES" para una BD especificada.


sábado, 26 de febrero de 2011

Ejecutando lotes de modificaciones (INSERT, UPDATE, DELETE) en BBDD

Esta me salvó hace poco de un proceso de modificaciones (INSERT, UPDATE y DELETE) eterno!

Si necesita realizar muchas operaciones de las comentadas, por ejemplo un par de millones, resulta bastante útil y reduce considerablemente el tiempo de respuesta, incluir BEGIN y COMMIT al comienzo y final del lote.

Con esto eliminamos sobrecarga realizando una sola transacción y no una por instrucción. Espero sea de utilidad... Recuerde: BEGIN; INSERT...; COMMIT;

Barajita premiada (#21) - número de días en un mes

Esta es una barajita breve, pero no por ello menos útil. Se trata de un método sencillo en *nix de obtener el número de días que tiene un mes:

$ month=02; year=2011
$ cal $month $year | xargs | awk '{print $NF}'
28

Obtengo los días en formato calendario, los paso por xargs para quitar retornos de carro/tabulaciones y luego pido el último elemento con AWK.


miércoles, 19 de enero de 2011

Instalando dos versiones de PHP-CLI en una misma máquina

El otro día tuvimos problemas instalando dos versiones del interprete de línea de comandos de PHP (PHP-CLI) en una misma máquina, específicamente, queríamos instalar en una máquina con Ubuntu 10.04 que ya tenía PHP5.2, PHP5.3.

Mi primo Daniel (http://dcestari.posterous.com/), quien es un crack como dicen por estos lares (en Venezuela diríamos un duro), dió con la solución...

Descargó el fichero con los fuentes de php.net y configuró utilizando las opciones que trae el .deb que descargas desde los repos. A continuación las opciones:

# ./configure --prefix=/usr/local/ --disable-cgi \
--program-suffix=5.3 \
--with-config-file-path=/etc/php5.3/cli \
--with-config-file-scan-dir=/etc/php5.3/cli/conf.d \
--build=$(dpkg-architecture -qDEB_HOST_GNU_TYPE) \
--host=$(dpkg-architecture -qDEB_HOST_GNU_TYPE) \
--sysconfdir=/etc \
--localstatedir=/var \
--disable-debug \
--with-regex=php \
--disable-rpath \
--disable-static \
--with-pic \
--with-layout=GNU \
--enable-calendar \
--enable-sysvsem \
--enable-sysvshm \
--enable-sysvmsg \
--enable-bcmath \
--with-bz2 \
--enable-ctype \
--with-db4 \
--without-gdbm \
--with-iconv \
--enable-exif \
--enable-ftp \
--with-gettext \
--enable-mbstring \
--with-pcre-regex=/usr \
--enable-shmop \
--enable-sockets \
--enable-wddx \
--with-libxml-dir=/usr \
--with-zlib \
--with-kerberos=/usr \
--with-openssl=/usr \
--enable-soap \
--enable-zip \
--with-mhash=yes \
--with-libedit \
--without-mm \
--enable-pdo \
--with-pdo-mysql \
--with-mysql --without-sybase-ct --with-sqlite \
--without-mssql --with-sqlite3 --enable-pcntl \
--without-pear

Luego, compilamos con make y lo novedoso.... en lugar de instalar el compilado con el típico make install, el descubrimiento del año (al menos para mi), utilizamos checkinstall lo que genera un .deb -con el nombre especificado- que después se puede instalar y desinstalar fácilmente con dpkg.


lunes, 10 de enero de 2011

Copiando permisos de una BD MySQL a otra

Este script lo encontré en la red y me pareció que debía publicarlo aquí por su gran utilidad:
#!/bin/bash
# adapted from (http://www.pyrosoft.co.uk/blog/2006/10/18/show-grants-for-all-users-on-mysql/)
mysql --batch --skip-column-names -e "SELECT user, host FROM user" mysql |
while read user host; do
  echo "# $user @ $host"
  mysql --batch --skip-column-names -e"SHOW GRANTS FOR '$user'@'$host'"
done

El script mostrado arriba saca todos los usuarios de la BD y luego retorna un script con los privilegios para cada uno de estos, lo que permitiría migrar privilegios y usuarios de una BD MySQL a otra.