lunes, 25 de octubre de 2010

Transformando un archivo de ids en X para reemplazar en queries del tipo SELECT ... WHERE id IN (X)

Otra útil... típico que tenemos un archivo con un montón de ids y queremos meterlos dentro de alguna query del tipo "WHERE field IN (X)", donde X está formado por todos los ids. Este comando retorna X.

$ awk -v _SQ="'" 'NR>1 { retorno = retorno ", " _SQ $0 _SQ } END { print retorno }' ids 

El archivo ids contiene un id por línea.

* Este ejemplo siempre agrega un id vacio ('') al comienzo, para quitarlo basta agregar un IF. Yo lo he dejado porque me conviene en la mayoría de casos.


Cómo enviar la salida de una query a un archivo separado por "comas" (mysql)

Esta es sencilla pero útil, cómo ejecutar una query y enviar su salida a un archivo separado por comas (o cualquier otro caracter). En PostgreSQL es trivial, pero en MySQL no existe (o al menos no lo he conseguido) un parámetro que defina un FS (field separator).

$ mysql -u root db_name < query | sed 's/\t/|/g' | tee output.csv
Dentro del archivo query está la QUERY. La salida, producto de ejecutar la query, la procesamos con sed reemplazando TABS (\t) por PIPES* (|).

* En lugar de PIPES podrían ser comas, pero no recomiendo ese caracter porque es muy común.

martes, 12 de octubre de 2010

Barajita premiada (#19) - cut & paste

Estos dos comandos son súper útiles cuando deseamos generar archivos CSV a partir de otros CSV. Sobre todo cuando necesitamos pegar columnas con otras (verticalmente). Por ejemplo:

rodolfo@rcampos-laptop:~/cutAndPaste$ cat a 
id|nombre|apellido|nombreCompleto
1|Rodolfo|Campos|Rodolfo Campos
2|Juan|Pérez|Juan Pérez
rodolfo@rcampos-laptop:~/cutAndPaste$ cat b
id|cédula
1|12345678
2|87654321
rodolfo@rcampos-laptop:~/cutAndPaste$ cut -d'|' -f2 b | paste -d '|' a -
id|nombre|apellido|nombreCompleto|cédula
1|Rodolfo|Campos|Rodolfo Campos|12345678
2|Juan|Pérez|Juan Pérez|87654321

En el ejemplo mostrado arriba hacemos lo siguiente:
  1. Cortamos la segunda columna del archivo "b" (opción -f), especificando el delimitador (-d).
  2. Pegamos la salida después del contenido de "a" y la separación de ambos archivos es marcada con un "|" (opción -d). Note que la salida que viene de la tubería (del comando anterior) se especifica como entrada del comando paste utilizando un guión.


Barajita premiada (#18) - sort

Sort, tan "sencillo" pero tan "poderoso".

El comando sort permite ordenar líneas de un flujo de bytes. Este ordenamiento se puede hacer considerando caracteres ASCII, numéricos, binarios, entre otros.

Por ejemplo:
rodolfo@rcampos-laptop:~/sort$ cat a
hello
1
3
44
4
5
foo
rodolfo@rcampos-laptop:~/sort$ sort a
1
3
4
44
5
foo
hello
rodolfo@rcampos-laptop:~/sort$ sort -n a
foo
hello
1
3
4
5
44

En el ejemplo mostrado arriba, el archivo "a" fue ordenado en orden alfabético y luego numérico (opción -n). Para ambos casos, puede observar la diferencia por las posiciones de las palabras y los números 4, 5 y 44.

Pero sort también puede ordenar un archivo con líneas con campos separados por algún caracter (Ej. CSV) rápidamente y sin mayores complicaciones. Esto sobre todo me fue de gran utilidad para ordenar un archivo que pesaba 1Gb y estaba separado por "|". Obviamente debido al tamaño, no resultaba trivial abrir el mismo con Excel y pedirle que ordenara las líneas por la columna X. Mire este ejemplo:

rodolfo@rcampos-laptop:~/sort$ cat a
name|id|type
first|3|1
second|1|1
third|2|2
fourth|0|2
rodolfo@rcampos-laptop:~/sort$ sed '1d' a | sort -t '|' -nk 2
fourth|0|2
second|1|1
third|2|2
first|3|1

En el ejemplo de arriba, es removida la primera línea del archivo con sed (1d) y luego ordenado de forma numérica (-n) considerando el segundo campo (-k 2) para un flujo con campos separados por "|" (opción -t).


jueves, 19 de agosto de 2010

Barajitas premiadas (#17) - encoding

Encodings, encodings, problemáticos todo lo que tenga que ver con ellos...

Típico que tenemos un archivo en ASCII y lo necesitamos en UTF-8, o UTF-16 o ISO8859-X, etc, etc.

Aquí les dejo 3 métodos que pueden utilizar para cambiar el encoding de un archivo:
  1. Abriendo el archivo con un bloc de notas (Ej. gedit) y seleccionando explícitamente el encoding (codificación) del archivo al "guardar como..."
  2. Utilizando el comando iconv, Ej. iconv -f ASCII -t UTF-8 -o UTF_FILE ASCII_FILE
  3. Utilizando VIM (en modo comando con el archivo abierto), la secuencia:
    :set bomb
    :set fileencoding=utf-8
    :wq


De todas las formas mencionadas especialmente me gusta la tercera. Y luego para verificar si todo se hizo: file IS_IT_REALLY_AN_UTF_FILE

Replicación Maestro->Esclavo1->Esclavo2 en mySQL

A continuación les dejo una chuleta (cheat sheet) para configurar bases de datos mySQL de forma Maestro->Esclavo1->Esclavo2.

Configuración inicial

Maestro: host localhost, port 17050
Esclavo1: host localhost, port 17051
Esclavo2: host localhost, port 17052

Configurando Maestro-Esclavo1

Primero: Configuración del Maestro
  1. Debe tener log-bin (bajo [mysqld] en my.cnf)
  2. Debe tener un usuario para replicación
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'localhost' IDENTIFIED BY '123456';
  3. Debe extraer los parámetros de configuración para Esclavo1
    SHOW MASTER STATUS;

Segundo: Configuración del Esclavo1
  1. Debe tener log-bin (bajo [mysqld] en my.cnf)
  2. Debe tener log-slave-updates (bajo [mysqld] en my.cnf)
  3. Debe tener un usuario para replicación
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'localhost' IDENTIFIED BY '123456';
  4. Debe configurar Esclavo1 como esclavo de Maestro, para lo cual deberá introducir los parámetros obtenidos arriba en el paso 3.
    CHANGE MASTER TO MASTER_HOST='localhost', MASTER_PORT=17050, MASTER_USER='repl', MASTER_PASSWORD='123456', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=202
  5. Debe comenzar la replicación
    START SLAVE;

Incorporando al Esclavo2

Una vez que el esquema Maestro-Esclavo1 se encuentre funcionando correctamente, se procederá a configurar el Esclavo2.

Primero: Extracción del dump (a partir de Esclavo1)
  1. Debe detener la replica como esclavo y liberar sus logs (en Esclavo1):
    STOP SLAVE;
    FLUSH LOGS;
  2. Debe extraer un dump (de Esclavo1)
    mysqldump -u root --password=msandbox --port=17051 --host=127.0.0.1 --master-data=2 test > dump1.sql
  3. Reiniciar la replica como esclavo (en Esclavo1 - volviendo a la normalidad):
    START SLAVE;

Segundo: Configuración del Esclavo2
  1. Restaurar el dump (en Esclavo2)
    mysql -u root --password=msandbox --port=17051 --host=127.0.0.1 test < dump1.sql
  2. Extraer parámetros de configuración del maestro. Para ello deberá buscar en el dump (dump1.sql) una línea como la siguiente:
    -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=106
  3. Configurar Esclavo2 como esclavo de Esclavo1. Deberá copiar los parámetros extraidos en el paso 2 e introducirlos en este comando:
    CHANGE MASTER TO MASTER_HOST='localhost', MASTER_PORT=17051, MASTER_USER='repl', MASTER_PASSWORD='123456', MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=106
  4. Iniciar el esclavo (en Esclavo2)
    START SLAVE;

NOTA: La configuración inicial Maestro-Esclavo se asume desde el inicio, antes de que la base de datos tuviese registro alguno.

Para este ejercicio utilicé MySQL Sandbox

Y para realizar inserciones -sin parar- en las tablas del maestro utilicé el siguiente script:

#!/bin/bash

x=0;     # initialize x to 0
#while [ "$x" -le 10 ]; do
while true; do
    mysql -u root --password=msandbox --port=17050 --host=127.0.0.1 -e "INSERT INTO test.a VALUES($x)"   
    # increment the value of x:
    x=$(expr $x + 1)
    sleep 2
done

OJO: La tabla que uso es muy sencilla: CREATE TABLE a (a int); dentro del esquema test que instala por defecto MySQL Sandbox.


jueves, 12 de agosto de 2010

Cómo escapar caracteres para manipularlos en una consola

Esto me dio bastantes problemas la verdad. Tenía un archivo con comandos SQL y otras cosas, entonces era común conseguir comillas simples y dobles por todos lados. Por cuestiones que no vienen al caso, necesitaba manipular cada línea con funciones AWK; el cual colapsaba con todos estos caracteres especiales.

Aquí la solución, primero el comando que me hizo ver la luz:

$ echo $(printf '%q' $line)

La función mostrada arriba escapa todos los caracteres especiales. Ahora sólo me quedaba depurar las líneas del archivo y ejecutar un comando para cada una de ellas:

#!/bin/bash
cat PROBLEMATIC_FILE |
while read line; do
  echo $(printf '%q' $line) |
  awk -v _SQ="'" '{
   # ... process
    print $0
  }' 
done