Bases de datos distribuidas en PostgresSQL

Una base de datos distribuida (BDD) es un conjunto de múltiples bases de datos lógicamente relacionadas las cuales se encuentran distribuidas en diferentes espacios lógicos (pej. un servidor corriendo 2 máquinas virtuales) e interconectados por una red de comunicaciones.

Dichas BDD tienen la capacidad de realizar procesamiento autónomo, esto permite realizar operaciones locales o distribuidas.

Un sistema de Bases de Datos Distribuida (SBDD) es un sistema en el cual múltiples sitios de bases de datos están ligados por un sistema de comunicaciones de tal forma que, un usuario en cualquier sitio puede acceder los datos en cualquier parte de la red exactamente como si estos fueran accedidos de forma local.

Un sistema distribuido de bases de datos se almacenan en varias computadoras. Los principales factores que distinguen un SBDD de un sistema centralizado son los siguientes:




www.datos.misiones.gov.ar


Desde cualquier BD se pueden recuperar datos de cualquier otra base de datos PostgresSQL (Naturalmente, es necesario identificarse con perfil de un usuario con acceso a los mismos) usando dblink. DBLink son funciones preparadas por los autores de postgres que añaden la funcionalidad de acceso a datos de forma remota.

No se trata de conectarse remotamente con un servidor de bases de datos, sino acceder desde una base da datos a elementos de otra base de datos a través de una linea de comunicaciones, combinando ambas

Instalar las funciones de dblink

Es un paso que hay que ejecutar SOLO UNA VEZ y que permite instalar las funciones necesarias para proceder a la comunicación con bases de datos remotas.

En la base de datos desde la que pretendemos conectarnos en remoto se ejecuta el SQL que creará las funciones de dblink. El script está en:

C:\Program Files\PostgreSQL\8.4\share\contrib\dblink.sql

Para las versiones 9.1 y posteriores, para generar las funciones de dblink basta ejecutar en SQL

CREATE EXTENSION dblink;


Ejecutar comandos de SQL

Comandos del tipo SELECT:

Los campos de las tablas del nodo remoto se “traen” al nodo local con el predicado FROM añadiendo los parámetros de conexión proporcionados por dblink

SELECT lista_de_campos
FROM dblink('dbname=baseDatos port=puerto host=ordenadorRemoto user=usuario password=contraseña','SQL') AS Alias(campo1 tipo1,campo2 tipo2,…)
WHERE condiciones
ORDER BY criterios_de_ordenacion;


Es necesario indicar en el Alias qué tipo de campos vienen desde el ordenador remoto, pues se desconoce. Este extremo se define en  AS …



Comandos de mantenimiento de tablas (INSERT/DELETE/UPDATE):

En este caso se realiza mediante la ejecución de función dblink_exec(conexion,sentencia) Por ejemplo, si queremos insertar valores en la tabla gente (codigo int PK, nombre varchar, sueldo double):
select dblink_exec('dbname=remota port=5432 host=yoquese.com user=postgres password=contrasena',
'INSERT INTO gente (codigo,nombre,sueldo) VALUES (33,\'Pepito\',1234.45));


Nota: \' sirve para incluir el carácter especial de comilla simple en la sentencia a ejecutar

Ejercicio 1.- Mostrar datos de otra base de datos residente en el mismo equipo:

SELECT p.nombre
FROM dblink('dbname=futbol user=postgres password=root','SELECT nombre FROM equipos') AS p(nombre varchar(100));

Ejercicio 2.- Mostrar los nombres de los empleados que comienzan por E en una tabla llamada templa de la base de datos empleado que está en el servidor del alumno 'pepito' (IP=192.168.32.44):

SELECT p.nombre,p.apellido
FROM dblink('dbname=empleado
        port=5432
        host=192.168.32.44
        user=postgres
        password=root',
         'SELECT nombre,apellido FROM templa')
        AS p(nombre varchar(100),apellido varchar(100))
WHERE p.nombre LIKE 'E%';

Ejercicio 3.- Listado de apellidos y departamentos donde trabajan los empleados de la misma base de datos anterior. En este caso las 2 tablas son remotas.

SELECT a.b,d.f
FROM     
dblink(    'dbname=ejemplo1 port=5432 host=192.168.32.44 user=postgres password=root' ,
'SELECT apellido,dept FROM templa') AS a(b varchar(100),c varchar(100)) ,
dblink(    'dbname=ejemplo1 port=5432 host=192.168.32.44 user=postgres password=root',
'SELECT numdep,nomdep FROM tdepta') AS d(e varchar(100),f varchar(100))
WHERE a.c=d.e; 

Ejercicio 4.- Mezcla de datos remotos y locales. Esta vez se hará en varios pasos

a) Conexión con la base de datos remota

SELECT dblink_connect('dbname=empleado port=5432 host=192.168.32.44 user=postgres password=root');

b) Sentencia SQL (mezclo una tabla remota con una local)

SELECT r.nombre,a.texto
FROM     dblink('SELECT nuempl,nombre FROM templa') AS r(nuempl varchar,nombre varchar),
tabla_local AS a
WHERE r.nuempl=a.codigo;

c) Cierre de la conexión remota

SELECT dblink_disconnect();

Ejercicio 5.- Realización de una copia de una tabla remota (o parte de ella)

SELECT dblink_connect('dbname=empleado port=5432 host=192.168.32.44 user=postgres password=root');
SELECT * into copia FROM dblink('SELECT nuempl,nombre FROM templa') AS r(nuempl varchar,nombre varchar);
SELECT dblink_disconnect();

Problemas a tener en cuenta:



Práctica de acceso a bases de datos distribuidas:

Se va a realizar una simulación del valor de una cartera de valores. Para realizarla será necesario que 4 alumnos creen en sus equipos (192.168.32.1 a 192.168.32.4) las siguientes bases de datos, tablas, registros y campos, es decir, la información se haya distribuida en 4 bases de datos remotas:

Equipo Base de datos Tabla Contenido
192.168.32.1 ibex35 cotizaciones
192.168.32.2  ftse quote  
192.168.32.3 dax30 zitat 
192.168.32.4 downjones  quote 
change  


En cada ordenador del aula disponemos de una base de datos llamada 'mipasata' con una tabla llamada 'cartera' con nuestra cartera de valores como la siguiente

 

 Solución.:
Todas las funciones reciben 2 parámetros llamados a y b

cuanto(varchar,varchar)  que(varchar,varchar)   cambio(char,char)
declare
    respuesta double precision;
    basedatos varchar(100);
    puerto varchar(4);
    ordenador varchar(100);
    usuario varchar(100);
    contrasena varchar(100);
    tabla varchar(100);
begin
if (b='ibex35') then
    basedatos='ibex35';
    puerto='5432';
    ordenador='192.168.32.1';
    usuario='postgres';
    contrasena='root';
    tabla='cotizaciones';
end if;
if (b='ftse') then
    basedatos='ftse';
    puerto='5432';
    ordenador='192.168.32.2';
    usuario='postgres';
    contrasena='root';
    tabla='quote';
end if;
if (b='dax30') then
    basedatos='dax30';
    puerto='5432';
    ordenador='192.168.32.3';
    usuario='postgres';
    contrasena='root';
    tabla='zitat';
end if;
if (b='downjones') then
    basedatos='downjones';
    puerto='5432';
    ordenador='192.168.32.4';
    usuario='postgres';
    contrasena='root';
    tabla='quote';
end if;
select into respuesta p.valor
from dblink('dbname=' || basedatos ||
    ' port=' || puerto ||
    ' host=' || ordenador ||
    ' user=' || usuario ||
    ' password=' || contrasena ,
    'select * from ' || tabla)
AS p(tic varchar(10),valor double precision,moneda varchar)
    where p.tic=a;
    return respuesta;
end;       
declare
    respuesta character;
    basedatos varchar(100);
    puerto varchar(4);
    ordenador varchar(100);
    usuario varchar(100);
    contrasena varchar(100);
    tabla varchar(100);
begin
if (b='ibex35') then
    basedatos='ibex35';
    puerto='5432';
    ordenador='192.168.32.1';
    usuario='postgres';
    contrasena='root';
    tabla='cotizaciones';
end if;
if (b='ftse') then
    basedatos='ftse';
    puerto='5432';
    ordenador='192.168.32.2';
    usuario='postgres';
    contrasena='root';
    tabla='quote';
end if;
if (b='dax30') then
    basedatos='dax30';
    puerto='5432';
    ordenador='192.168.32.3';
    usuario='postgres';
    contrasena='root';
    tabla='zitat';
end if;
if (b='downjones') then
    basedatos='downjones';
    puerto='5432';
    ordenador='192.168.32.4';
    usuario='postgres';
    contrasena='root';
    tabla='quote';
end if;
select into respuesta p.moneda
from dblink(' dbname=' || basedatos ||
    ' port=' || puerto ||
    ' host=' || ordenador ||
    ' user=' || usuario ||
    ' password=' || contrasena ,
    'select * from ' || tabla)
AS p(tic varchar(10),valor double precision,moneda varchar)
    where p.tic=a;
    return respuesta;
end; 
declare
    respuesta double precision;
begin
if(a=b) then
     return 1;
else
     select into respuesta p.r3
     from dblink(' dbname=downjones
    port=5432
    host=192.168.32.4
    user=postgres
    password=root' ,
    'select * from change')
AS p(r1 varchar,r2 varchar,r3 double precision)
    where p.r1=a
    and p.r2=b;
  if(respuesta>0) then
    return respuesta;
  else
        select into respuesta p.r3
        from dblink(' dbname=downjones
        port=5432
        host=192.168.32.44
        user=postgres
        password=root' ,
         'select * from change')
        AS p(r1 varchar,r2 varchar,r3 double precision)
        where p.r1=b
        and p.r2=a;
        return (1/respuesta);
    end if;
  end if;
end;

Para realizar un listado de la cartera de valores haremos la consulta:

select cantidad, tic, mercado, cuanto(tic,mercado), que(tic,mercado), cambio(que(tic,mercado),'E')*cantidad*cuanto(tic,mercado)
from cartera;

que dará como como resultado:



 

Ejercicio de bases de datos distribuidas:

El ordenador remoto lamerced.no-ip.org dispone un servidor postgreSQL con una base de datos llamada staples. Se ha creado un usuario llamado 'vendedor' con la contraseña 'venta' que puede acceder a las siguientes vistas

1.- Realizar una consulta sql que permita obtener las entradas disponibles y el precio de las mismas

select * 
from         
        dblink('dbname=staples port=5432 host=lamerced.no-ip.org user=vendedor password=venta','select * from entradas_partido') AS e(fila integer,asiento integer,ocupado boolean),

        dblink('dbname=staples port=5432 host=lamerced.no-ip.org user=vendedor password=venta','select * from precios_partido') AS p(fila integer,importe double precision)
where
        e.fila=p.fila

        and e.ocupado=false;

 2.- Crear en nuestro servidor local una tabla llamada comisiones con los siguientes campos:

En esta tabla pondremos que la comisión por vender una entrada de la fila 1 es el 20% (0.2) y para la fila 2 nos llevamos el 10% (0.1)
 
3.- Realizar una función llamada vendiendo en nuestro servidor con las siguientes características:

 

declare
            r boolean;
            pr double precision;
            sentencia character varying;
            nada character varying;
begin

    --Veamos si la entrada está disponible

    select into r e.ocupado
    from dblink('dbname=staples port=5432 host=lamerced.no-ip.org user=vendedor password=venta','select * from entradas_partido') AS e(fila integer,asiento integer,ocupado boolean)
    where e.ocupado=false
    and e.fila=f
    and e.asiento=a;

    --Dependiendo de la instruccion anterior r puede ser false (disponible, true (ya vendida) o null (no existe esa entrada
)

    if(r=false) then
        --Calculamos el importe de la entrada
        select into pr p.importe*(1+c.comision)
        from     dblink('dbname=staples port=5432 host=lamerced.no-ip.org user=vendedor password=venta','select * from precios_partido') AS p(fila integer,importe double precision), 
                    comisiones c

        where c.fila=f
        and p.fila=f;

       
--Cambiamos ocupado de false a true

        sentencia='update entradas_partido set ocupado=true where fila='||f||' and asiento='||a;
        select into nada dblink_exec('dbname=staples port=5432 host=lamerced.no-ip.org user=vendedor password=venta',sentencia);

        --Devolvemos el mensaje de la venta

        return ('VENTA EFECTUADA. Fila: '||f||'. Asiento: '||a||'. Importe: '||pr);
    else
        --Esa entrada o no existe o no esta disponible
         return('ENTRADA NO DSIPONIBLE');
    end if;
end;