jueves, 13 de septiembre de 2018

Instalación de Londiste3, puesta en marcha y un poco más allá.

En estos meses he estado trabajando en un proyecto de instalación de londiste3 en una base de datos distribuida. Durante todo el proceso fue muy obvio que no había documentación alguna al respecto en la web y la que conseguí no era correcta aunque me ayudo en algo.

Para evitarles el sufrimiento voy a agregar el paso a paso de que fue lo que hice para que puedan realizar una instalación de londiste3 y entiendan un poco como funciona. Es posible que yo no me haya topado con algunos errores que tal vez ustedes encuentren pero pueden consultar y tal vez, solo tal vez tenga la respuesta.

Iniciemos por lo básico. Qué es londiste (búscalo en la web si estas aquí es por que ya lo sabes. xD)

En resumidas cuentas, es una herramienta que te permitirá sincronizar tus bases de datos postgresql, te permite enviar la información de una base de datos raiz "root" a otras ramas ("branch") o hojas ("leaf").

Un punto importante es que mientras existan estas conexiones las tablas involucradas solo podrán ser escritas en el nodo root, en el resto de las bases de datos quedaran bloqueadas para escritura, por lo que solo podrás leer los datos pero no editarlos.

Otro punto a tener en cuenta es que la replicación tiene un costo en tu servidor root, este no solo estará guardando datos, sino que también escribir los triggers que enviaran los datos al resto de los nodos, por lo que el coste de escritura se cuadruplica (Es lo que tengo entendido, no soy experto en esta área pero podrás encontrar información al respecto si le preguntas a nuestro amo y señor google).

Ahora, respecto a la instalación, es necesario mencionar que para la misma yo use ubuntu14.04, postgresql9-3, y skytools-3.2. Te recomiendo leer toda la entrada primero, incluido los link, sin realizar ninguna acción para que al releer y seguir los pasos tengas mas claro como resolver los distintos problemas que se te puedan presentar.

Puedes seguir los pasos para la instalación de este link: instalar londiste3 (si tienes problemas al instalar, vuelve aquí)

Y si la página ha desaparecido no dudes en contactarme, guarde su contenido en mi correo. (la precaución ante todo).

Puede que consigas un problema en esta instrucción:

sudo pip install psycopg2

Error conocido (undefined symbol: PyUnicodeUCS4_AsUTF8String)
Solución: Desinstale psycopg2 e instale a través de easy_install.
sudo su - webuser
source serverApp_py2env/bin/activate
pip uninstall psycopg2
easy_install psycopg2==2.5.3

Al menos a mi me funcionó, espero que no pases trabajo y te sea de utilidad.

Si has seguido todos los pasos previos y te esta trabajando londiste3, felicidades. No has hecho más que comenzar, porque ahora iremos a lo avanzado.

Te pediré que primero le eches un vistazo a este link (el que espero que siga ahí para cuando lo veas, son imágenes de una presentación entre londiste2 y londiste3, no tengo copias de ello pero explicaré algo más delante): skype londite2 y londiste3

Si notaste bien, existen tres tipos de nodos, root, branch y leaf y cada uno de ellos trabaja diferente.

root, como ya habrás notado es quien se encarga de enviar los datos.
branch y leaf reciben esos datos.
branch puede trasmitir sus datos y también puede intercambiar su posición con root

Las laminas 15-17 pueden ser bastante ilustrativas, mas si sigues estos ejemplos fallaran porque las instrucciones no están actualizadas.

Así que vamos a actualizar un poco estas laminas:

Si ya instalaste y configuraste londiste, entonces necesitaras restablecer el servicio, ve hasta la parte en la que indico como borrar las configuraciones de londiste para el nodo root, en caso de que te de problemas el resto de los nodos o las ip hayan cambiado, para el resto también servirá.

Vale, ahora ubícate desde el terminal en la carpeta que contiene los archivos .ini

En las instrucciones hay algo en lo que no son para nada claros, y es en estos archivos.

Veamos nuestra imagen:

Para esta estructura debes crear los siguientes archivos (si es que ya no los tienes):

Cada uno de las bases de datos debe tener su propio .ini
db_alfa.ini
db_bravo.ini
db_delta.ini
db_gama.ini

Esta es su estructura:
[londiste3]job_name=file_name
# target databasedb = user=postgres password=password host=localhost dbname=db_name # queue namequeue_name = db_name

# how many seconds to sleep between work loops# if missing or 0, then instead sleeping, the script will exitloop_delay = 0.1
# where to loglogfile = log/file_name.logpidfile = pid/file_name.pid

Note que file_name hace referencia a un nombre único para cada conexión y db_name es el nombre de la base de datos a replicar, por lo que debe ser la misma en todos los archivos.

Una vez tienes los cuatro archivos debes crear los archivos ticker_file_name.ini (Importante: ¿Cómo sabes cuantos debes crear? Uno para root, y uno por cada branch, ignorando los leaf). En nuestro ejemplo serán dos, uno root y otro branch.

ticker_alfa.ini
ticker_bravo.ini

Esta es su estructura:
[pgqd]# libpq connect string without dbname=base_connstr = user=postgres password=password host=localhost # limit ticker to specific databasesdatabase_list = db_name
 # where to loglogfile = log/ticker_file_name.log # pidfilepidfile = pid/ticker_file_name.pid

db_name debe ser el mismo nombre que has colocado en el resto de los archivos .ini y recuerda especificar un nombre distinto donde va el ticker_file_name, que sea el nombre del archivo para que no te compliques.

Bien, con los archivos.ini listos es momento de crear los nodos. Te recomiendo usar el mismo nombre de los archivos file_name.ini como nombre, sin el .ini claro esta.
No colocare las sentencias exactas, es decir con los nombres de los archivos, por que te hará bien practicar y revisar que lo estas haciendo correctamente, además necesitas estar consciente de que nombre lleva cada nodo.

Otro dato importante es que en estos ejemplos estoy usando "localhost", pero esto no tendría ningún sentido en el mundo real así que asegúrate de que has agregado las configuraciones necesarias en los archivos pg_hba.conf y postgresql.conf.

Busca y modifica las siguientes lineas en cada uno de los servidores involucrados:

sudo gedit /etc/postgresql/9.3/main/pg_hba.conf
# IPv4 local connections:
host    all             all             192.168.1.0/24            md5

Esto permitirá la conexión a todos los usuarios a todas las bases de datos a todos los equipos dentro de la red 192.168.*.*

sudo gedit /etc/postgresql/9.3/main/postgresql.conf
listen_addresses = '*' # what IP address(es) to listen on;

Para mayores de estas configuraciones pregúntale a nuestro amo y señor san google.

Ahora...

Creamos el nodo root:
Su sintaxis es (¡Cuidado! esta en dos lineas, pero va en una sola linea):
londiste3 file_name.ini create-root file_name 'user=postgres password=password host=localhost
  dbname=db_name'

Creamos el nodo branch:
Su sintaxis es (¡Cuidado! esta en tres lineas, pero va en una sola linea):
londiste3 file_name.ini create-branch file_name 
 'user=postgres password=password host=localhost dbname=db_name' --provider='user=postgres password=password host=localhost dbname=db_name'

Creamos los nodos leaf:
Su sintaxis es (¡Cuidado! esta en tres lineas, pero va en una sola linea):
londiste3 file_name.ini create-leaf file_name
 'user=postgres password=password host=localhost dbname=db_name' --provider='user=postgres password=password host=localhost dbname=db_name'

Si algo te ha fallado debes revisar las lineas que has escrito en los comandos, los archivos .ini y las configuraciones de postgresql. Es muy, muy probable que en alguna de ellas este el error.


Ahora vamos a la parte divertida, iniciar los servicios. Debes estar ubicado en la ruta donde tienes los archivos .ini así que has un "cd directorio/". Los ticker los puedes iniciar con las siguiente instrucción (recuerda que tienes dos):
pgqd -d ticker_file_name.ini

El -d sirve para iniciar como demonio el servicio, o en segundo plano si lo prefieres. usa pgqd --help para conocer todas sus opciones. Y ten en cuenta que -s sirve para detener el servicio y -k para matarlo.

Puedes tener problemas si el servicio esta iniciado pero no responde. En ese caso, ¿recuerdas qué agregaste un pid en los archivos? busca la ruta y elimina los archivos .pid (rm *.pid) si no responden a los comandos kill o stop, también has un sudo killall pgqd para que no quede un proceso innecesario corriendo.

Subamos los trabajadores (workers) y recuerda que son cuatro archivos en este ejemplo:
londiste3 -d file_name.ini worker

Al igual que el servicio anterior -d ejecutara el worker en segundo plano. dispones de -k para matar el proceso y -s para detenerlo normalmente. Si presentas problemas, ya sabes que hacer, busca los pid, eliminalos y por si acaso un "sudo killall londiste3" matara todos los procesos que estén corriendo y no puedas detener. Un "londiste3 --help" puede darte mas luces acerca de los comandos.

Importante: cuando se ejecutan tareas de londiste se crean unos archivos .pid con nombres un poco mas extensos que los especificados en el archivo. En ocasiones necesitaras eliminar estos archivos si no se detiene el proceso o si no te permite ejecutar una nueva instancia o un nuevo trabajo. Sobre todo si notas que ejecutar un comando londiste3 y parece no hacer nada, quedando estático. Si no esta esperando que se inicie el worker es que tuvo problemas por instancias previas que no se cerraron correctamente. Elimina esos archivos y podrás ejecutar nuevamente el comando, si sigue dando error, algo más esta pasando.

Bien, trabajadores corriendo, ticker corriendo. Es momento de agregar las tablas, podemos hacerlo con tablas especificas o simplemente decirle agrega todas las tablas. Esta es la sistaxis:
londiste3 file_name.ini add-table --all
londiste3 file_name.ini add-table table1 table2 table3

Asegúrate de agregar las tablas primero en los proveedores, osea primero en root y luego en branch y finalmente en las leaf. Esto por que si intentas agregar en las leaf o la branch, como no están disponibles en su proveedor respectivo, te dará un mensaje de "esto no tiene sentido" o "no están disponibles en el proveedor" o algo similar y no agregara las tablas.

¡Bien! ya esta todo arriba y debería estar marchando. Si ejecutas desde cualquier nodo lo siguiente te dirá el estado de la replicación.
londiste3 file_name.ini status

Debes ver algo como:

db_alfa (root)
    | Tables: 5/0/0
    | Lag: 3s, Tick: 1909
    +–: db_bravo (branch)
    | Tables: 0/5/0
    | Lag: 3s, Tick: 1909
    +–: db_delta (leaf)
    Tables: 0/5/0
    Lag: 3s, Tick: 1909

Esto es un ejemplo visual. Sin embargo, hay varias cosas aquí que te pueden indicar si todo esta marchando bien (y esto me toco descubrirlo golpeando la cabeza contra la pared y hay cosas que aun no entiendo). 

Lo primero: Tables: 5/0/0 te da una idea del estado de la sincronización. Si, root debe estar siempre en el primer puesto de la lista. Si se encuentra en el segundo o el tercero significa que no esta sincronizando, de hecho parece que el tercero significa que el worker no esta trabajando o no esta enlazado a ningún otro, de esto no estoy seguro, pero si que este en el segundo es malo, estar en el tercero es todavía peor. Normalmente el problema de estar en el segundo lugar de la lista se debe a que algo pasa con tu archivo ticker correspondiente, dirección ip equivocada, nombre equivocado, algo esta errado. Si esta en el tercero, te recomiendo borrar el nodo, revisar las configuraciones y volverlo a crear (Más abajo explico como).

Si se trata de una branch o un leaf, entonces debe estar en la segunda posición y según vaya sincronizando tablas pasaran a la primera posición. Tables: 2/3/0 pero igual que el anterior, si esta en el tercero, recrea el nodo. Si alguien tiene una solución menos drástica seré todo ojos, suponiendo que lo escriba en los comentarios.

Ahora, que todo esta trabajando puedes incluso forzar la sincronización si te parece que esta tardando en empezar.
londiste3 file_name.ini resync --all
Con esto, por lo que he visto, se revisaran todos los cambios en los datos de las tablas.

Por ejemplo si en una tabla has cambiado el nombre de un dato "maria" por "pedro" esto refrescara esos datos.
Pero este comando debes ejecutarlo desde los nodos branch o leaf y nunca desde root o se perderá la sincronización quedando en estatus Tables: 0/5/0.

En cambio este otro comando actúa de dos formas diferentes, busca los cambios recientes, nuevas filas en la tabla y me parece que también revisa si hay cambios en la estructura de la tabla (de esto ultimo no estoy seguro).
londiste3 file_name.ini compare
Es importante mencionar que este comando debe ejecutarse desde el nodo proveedor, desde root. No se cual es el efecto al ejecutarlo desde branch teniendo hojas (leaf) pero si se que al ejecutarlo desde la branch solo teniendo un nodo root el estado de ambos nodos quedara en la segunda posición de la lista Tables: 0/5/0,  por lo que no se sincronizaran. Así que sugiero usarlo siempre desde el nodo root.


Ahora intentemos cambiar el flujo de los datos. Queremos que db_beta tome la posición de db_alfa y para ello ejecutamos:
londiste3 db_bravo.ini takeover db_alfa
Importante: los worker y los ticker deben estar ejecutándose, si por alguna razón has detenido el servicio el comando quedara en espera hasta que los subas nuevamente.

Ejecutar este comando resuelve en algunos casos el problema del estatus Tables: 0/5/0 en root. Aunque tendrías que hacerlo dos veces para volver a la posición inicial, claro invirtiendo las posiciones de alfa y bravo.

Con este comando que hemos ejecutado abremos pasado del estado 1 (en la imagen) al estado 2


Notaras que ahora el flujo viaja desde db_bravo hasta db_alfa pero alfa sigue siendo el proveedor de las hojas db_delta y db_gama y eso es posible por que se trata de un nodo tipo branch. Aun así podemos cambiar este flujo ejecutando:
londiste3 db_gama.ini change-provider --provider db_bravo

Al ejecutar este comando terminaras, visualmente, como se muestra en la imagen posición 3. Si lo haces una vez mas pero con delta entonces terminaras como la posición 4 de la imagen arriba (Ese te lo dejo de prueba).

Nuevamente te recomiendo usar "londiste3 --help" para que revises cuidadosamente las opciones del comando ya que hay mucho por aprender y tal vez no todo aquí se ajuste a tus necesidades.

Otros dos comandos que tal vez quieras tener en cuentas son los siguientes, en la ayuda explican que hacen pero no como se usan así que te dejo la tarea de saber para que son y te ahorro la molestia de averiguar como se usan. (Tal vez corrijan el problema de la sincronización cuando esta en el segundo puesto de la lista en estatus, ademas creo pueden usarse con -r cuando el worker esta ejecutándose)
londiste3 file_name.ini worker --reset
londiste3 file_name.ini worker --rewind

Ahora si deseas eliminar las tablas que tienes asociadas a un nodo solo basta con usar el comando:
londiste3 file_name.ini remove-table --all
londiste3 file_name.ini remove-table table1 table2

Ten en cuenta que si lo ejecutas en un nodo que sirve como proveedor, se eliminara también en los nodos suscritos a el.

Si deseas eliminar un nodo:
londiste3 file_name.ini drop-node file_name
Usa el mismo nombre de archivo y nodo que deseas eliminar.

Por alguna razón extraña a los chicos de skype no se les ocurrio que uno pudiera querer eliminar el nodo root, tal vez por mala configuración o algún problema "X" en él o solo por diversión. Un fallo interesante. Si, eso significa que no puedes eliminar el nodo root, pero ¡hey! no lo puedes hacer desde londiste3 pero hay otra forma, la manera drástica que les mencionaba antes y es esta:
sudo -u postgres psql -c "DROP SCHEMA IF EXISTS londiste CASCADE;"sudo -u postgres psql -c "DROP SCHEMA IF EXISTS pgq CASCADE;"sudo -u postgres psql -c "DROP SCHEMA IF EXISTS pgq_ext CASCADE;"sudo -u postgres psql -c "DROP SCHEMA IF EXISTS pgq_node CASCADE;"
El orden es bastante importante y deben ejecutarlo en los nodos que les generen problemas y no consigan resolverlo de la manera convencional. No se preocupen, no perderán nada de su base de datos, esa esta en otro esquema.


Creo que puedo decir que eso es todo lo que conozco de londiste3. Si recuerdo algo más entonces actualizare esta entrada y ni siquiera lo notarán. Si conoces algo más al respecto, deja tu comentario y tal vez podamos hacer de esto algo más completo.


martes, 22 de mayo de 2018

Restablecer las secuencias ID en la base de datos postgresql

Normalmente después de restablecer la base de datos las secuencias de id se pierden provocando errores al momento de escribir en la base de datos. Para corregir todos estos errores cree el siguiente script el cual se ejecuta en la base de datos con esquema publico y agrega tablas a excluir. Ademas dejo dos scripts simples con los cuales comprobar si se corrige el problema.

--Corregir secuencias de id luego de restaurar
DO
$$
DECLARE
v_table VARCHAR;
v_table_cp VARCHAR;
v_table_block VARCHAR;
v_sql_string VARCHAR;
v_exclude_table VARCHAR[] := ARRAY['spatial_ref_sys', 'geometry_columns'];
v_include_table VARCHAR[];
v_i INTEGER;
v_num INTEGER;
v_max INTEGER;
v_next INTEGER;

BEGIN
SELECT INTO v_num count(relname)
FROM pg_class c
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(v_exclude_table) AND
nsp.nspname = 'public'
AND relhasindex = 't';

v_include_table := ARRAY(SELECT c.relname
FROM pg_class c
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(v_exclude_table) AND
nsp.nspname = 'public'
AND relhasindex = 't');

FOR v_i IN 1..v_num
LOOP

BEGIN
v_table := 'SELECT MAX(id) FROM ' || v_include_table[v_i];
EXECUTE v_table INTO v_max;

-- Then run...
-- This should be higher than the last result.
v_table_cp := 'SELECT nextval(''' || v_include_table[v_i] ||'_id_seq'')'; -- || ') INTO v_next';
EXECUTE v_table_cp INTO v_next;

-- If it's not higher... run this set the sequence last to your highest id.
-- (wise to run a quick pg_dump first...)
IF (v_next < v_max) THEN

-- protect against concurrent inserts while you update the counter
v_table_block := 'LOCK TABLE '|| v_include_table[v_i] ||' IN EXCLUSIVE MODE';
EXECUTE v_table_block;
-- Update the sequence
v_sql_string := 'SELECT setval('''|| v_include_table[v_i] || '_id_seq'', COALESCE((SELECT MAX(id)+1 FROM ' || v_include_table[v_i] || '), 1), false)';

EXECUTE v_sql_string;
RAISE NOTICE 'Table %s correct sequence',v_include_table[v_i];
END IF;
EXCEPTION WHEN others THEN
RAISE NOTICE 'Table %s ignore',v_include_table[v_i];
END;

END LOOP;
END
$$
--Para validar cambie tabla por el nombre de la tabla en ambos scripts sql
ultimo id de base de datos para la tabla
--SELECT MAX(id) FROM tabla
id que se genera al insertar un nuevo registro
--SELECT nextval('tabla_id_seq')

jueves, 3 de mayo de 2018

Truntate cascade, limpiar todas las tablas de un esquema

Este es muy parecido a la entrada anterior, solo que en vez de eliminar las tablas las deja limpias.
esto lo hago para luego cargar en la base de datos un respaldo sin que alguna tabla interrumpa la carga por tener datos previos.
Para ello realizo el respado con la siguiente instrucción:
<
sudo -u postgres pg_dump -a -Fp --exclude-table=field1 --exclude-table=field2 -v basedata_name > ~/Documentos/Respaldo_DB/dbbackup.sql
>

~/ me lleva directo al /home/usuario
<
DO
$$
DECLARE
v_table VARCHAR;
v_sql_string VARCHAR;
v_exclude_table VARCHAR[] := ARRAY['spatial_ref_sys', 'geometry_columns'];
v_include_table VARCHAR[];
v_i INTEGER;
v_num INTEGER;

BEGIN
SELECT INTO v_num count(relname)
FROM pg_class c
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(v_exclude_table) AND 
nsp.nspname = 'public'
AND relhasindex = 't';

v_include_table := ARRAY(SELECT c.relname
FROM (
SELECT pg_constraint.confrelid, COUNT(confrelid) as total FROM pg_constraint, pg_class WHERE
pg_class.oid = pg_constraint.conindid
GROUP BY pg_constraint.confrelid
) AS rest
RIGHT JOIN pg_class c ON rest.confrelid = c.oid
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(v_exclude_table) AND 
nsp.nspname = 'public'
AND relhasindex = 't'
ORDER BY 
CASE WHEN rest.total IS NULL THEN 0 END, rest.total);

FOR v_i IN 1..v_num
LOOP

v_sql_string := 'TRUNCATE TABLE ' || v_include_table[v_i] || ' CASCADE';
EXECUTE v_sql_string;
RAISE NOTICE 'Table %s Truncate',v_include_table[v_i];

END LOOP;
END
$$
>

restauro la base de datos de la siguiente forma:
<
sudo -u postgres psql basedata_name < ~/dbbackup.sql > dbackup.log
>

dbackup.log contiene un log de la ejecución del comando.

miércoles, 2 de mayo de 2018

Borrar grupo de tablas PostgreSQL sin que interfieran las restricciones

Estuve algunos días dando vuelta para poder eliminar un grupo de tablas exceptuando solo un grupo en especifico, un grupo muy pequeño de hecho. Conseguí mucha información pero nada lo suficientemente acertado ni practico para aplicarlo así que con esas gotas que fui consiguiendo construí esto

<
DO
$$
DECLARE
v_table VARCHAR;
v_sql_string VARCHAR;
v_exclude_table VARCHAR[] := ARRAY['field1', 'field2', 'field3'];
v_i INTEGER;
v_num INTEGER;

BEGIN
SELECT INTO v_num count(relname)
FROM pg_class c
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(v_exclude_table)
AND nsp.nspname = 'public'
AND relhasindex = 't';

FOR v_i IN 1..v_num
LOOP

SELECT c.relname
INTO v_table
FROM (
SELECT pg_constraint.confrelid, COUNT(confrelid) as total FROM pg_constraint, pg_class WHERE
pg_class.oid = pg_constraint.conindid
GROUP BY pg_constraint.confrelid
) AS rest
RIGHT JOIN pg_class c ON rest.confrelid = c.oid
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(v_exclude_table)
AND nsp.nspname = 'public'
AND relhasindex = 't'
ORDER BY 
CASE WHEN rest.total IS NULL THEN 0 END, rest.total LIMIT 1;
IF EXISTS(SELECT 1 FROM pg_class c
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(v_exclude_table)
AND nsp.nspname = 'public'
AND relhasindex = 't' ORDER BY c.oid desc LIMIT 1) THEN
v_sql_string := 'DROP TABLE ' || v_table;
EXECUTE v_sql_string;
RAISE NOTICE 'Table %s Deleted',v_table;
ELSE
RAISE NOTICE 'Table %s Not Deleted',v_table;
END IF;

END LOOP;
END
$$
>

Estos script SQL me permitieron buscar las tablas del esquema que me interesaba y organizarla de tal forma que las restricciones no interrumpieran su eliminación.

/*
SELECT relname
FROM pg_class c
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(ARRAY['field1', 'field2', 'field3'])
AND nsp.nspname = 'public'
AND relhasindex = 't';


SELECT c.relname, rest.total, c.oid
FROM (
SELECT pg_constraint.confrelid, COUNT(confrelid) as total FROM pg_constraint, pg_class WHERE
pg_class.oid = pg_constraint.conindid
GROUP BY pg_constraint.confrelid
) AS rest
RIGHT JOIN pg_class c ON rest.confrelid = c.oid
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE c.relname <> ALL(ARRAY['field1', 'field2', 'field3'])
AND nsp.nspname = 'public'
AND relhasindex = 't'
ORDER BY 
CASE WHEN rest.total IS NULL THEN 0 END, 
    rest.total;
*/

Entre tanto buscar perdí muchas de las referencias...

https://stackoverflow.com/questions/29913856/how-to-drop-a-table-based-on-if-condition-in-postgres
https://stackoverflow.com/questions/4326343/mysql-left-join-order-by-null-values-to-the-end