Salta el contingut

2. Bases de dades Objecte-Relacionals

1. Base de dades Objecte-Relacional

En aquesta secció treballarem amb la base de dades PostgreSQL. Com el sistema de gestió de bases de dades objecte-relacional de codi obert, que ha tingut el major impacte, sent un rival directe del gran Oracle.

Ens centrarem en les novetats que ofereix Postgres enfront de MySQL, com una extensió dels conceptes del primer any.

Important

Serà interessant:

  • Instal·lar el teu propi servidor PostgreSQL. Es recomana utilitzar-lo mitjançant un contenidor Docker.
  • Instal·lar un client per mostrar la teva base de dades. Recomanem DBeaver.

Treballarem amb aquest conegut exemple:

![Figuras](./img/Figuras.png){width=90%}

2. Definició de tipus

En PostgreSQL existeixen els tipus habituals que es troben en els SGBD moderns. A més, destaquen tipus especials per emmagatzemar adreces d'Internet (Tipus d'Adreça de Xarxa), XML i JSON per guardar i processar aquests formats, tipus propis i col·leccions. Amb això, podem observar els grans avanços de PostgreSQL, que d'una manera relaxada podríem considerar com Objecte-Relacional i Documental.

2.1. Enumeració

Els tipus enumerats són tipus que només permeten valors específics, generalment també coneguts com a dominis. En PostgreSQL els podem crear de la següent manera:

SQL
CREATE TYPE enum_name AS ENUM
    ( [ 'valor' [, ... ] ] );

per exemple:

SQL
create type basic_colors as enum('#FF0000','#00FF00','#0000FF');
create type street_kind as enum ('Calle','Avenida','Partida');

Aquesta manera de definir aquest tipus evitarà les comprovacions de valors (clàusules CHECK) existents en alguns SGBD relacionals.

2.2. Tipus estructurats

Els tipus estructurats són els precursors dels objectes en si. Si recordem, en la programació estructurada, a partir dels tipus bàsics, podíem crear estructures de dades:

  • On tots els elements són iguals (vectors, arrays i col·leccions)
  • On els seus elements podrien ser de diferents tipus. Aquestes estructures van evolucionar cap als objectes actuals afegint comportament i altres característiques.

En el model relacional, com que hem de respectar l'atomicitat de les dades, no podíem generar aquestes estructures. En Postgres podem crear aquests nous tipus de dades estructurats amb la següent sintaxi, molt semblant a la creació d'una taula:

SQL
1
2
3
4
CREATE TYPE type_name AS(
  attribute_name data_type
  [, ... ]-- one or more
);

per exemple:

SQL
1
2
3
4
create type Punto as(
    x integer,
    Y integer 
);

o, fent servir un una enumeració, de les vistes abans:

SQL
1
2
3
4
5
create type Address as(
    Type street_kind,
    street varchar,
    number int
);

i finalment creariem la següent taula:

SQL
create table person(
    idPerson serial primary key,
    name varchar,
    direction Address
);

insert into person(name) values ('joange');
insert into person(name,address) values('joange',null);
insert into person(name,address) values('joange',('Street','Calvary',1));

select direction from person;
select (address).street from person;

Recordatori

  • No es poden aplicar restriccions NOT NULL, DEFAULT o CHECK als camps que pertanyen als tipus creats.
  • La tipificació té sentit en dades que no existeixen per si mateixes, que necessiten ser incrustades en altres estructures o taules.
  • Quan s'utilitza dins d'una taula i es manipula la inserció, es farà en un bloc, entre parèntesis, ja que determina una estructura.
  • Per seleccionar un subtipus, hem d'envoltar el tipus general entre parèntesis, en cas contrari Postgres el confon amb una taula i genera un error.

3. Classes (o realment taules?)

Anem a crear una classe Figura, que serà el punt de partida d'una herència del model presentat a l'inici de la unitat. Vegem què incorpora respecte a les implementacions del model relacional. Figura conté una clau primària, i contindrà un Punto per localitzar-la en el pla. A més, conté una col·lecció de colors, per fer possibles degradats, guardats com un array. Guardar col·leccions és també una capacitat afegida que no suporta el model relacional, donada l'absència de valors multivaluats.

Es crea una classe amb la mateixa sintaxi que una taula, ja que, a efectes pràctics, des d'un punt de vista estructural és el mateix. Posteriorment, l'herència distingeix que una taula "hereta" d'una altra.

SQL
1
2
3
4
5
create table Figura (
    fID serial primary key, -- identifier
    Punto position,  -- position he occupies
    color TEXT[ ]    -- color(s) of the figure
);

Per inserir nous registres, tingues en compte que:

  • Els elements de tipus Punto s'han d'emmagatzemar utilitzant un constructor que crea una fila abstracta, anomenada ROW o entre parèntesis.
  • Per als arrays, també necessitem un constructor anomenat ARRAY, amb una llista d'elements.
SQL
insert into Figura(position,color)
  values(ROW(0,0),ARRAY['#FFFFFF','#00CC00']);

A partir d'aquí, crearem noves classes per representar els cercles, quadrats i rectangles de Figura utilitzant l'herència. La sintaxi és la següent:

SQL
1
2
3
create table sub_table(
 -- definition of table attributes
) inherits (super_table);

Com podem veure, simplement afegim inherits per crear la relació d'herència. Pel disseny que teníem anteriorment:

SQL
create table Rectangle(
    height int,
    width int 
) inherits (Figure);

create table Quadrat(
    side int
) inherits (Figure);

create table Cercle(
    radio int
) inherits (Figure);

Inserim algunes files, tenint en compte que també hem d'incloure els atributs de la superclasse.

SQL
1
2
3
4
5
6
insert into Quadrat(position,color,side) values (ROW(10,10),ARRAY['#00BBCC','#BBCC00'],40);
insert into Quadrat(position,color,side) values(ROW(10,fifteen),ARRAY['#AA6633','#CCFF00'],27);
insert into Cercle(position,color,radius) values(ROW(30,25),ARRAY['#BBCC','#CCCC00'],20);
insert into Cercle(position,color,radius) values(ROW(10,-10),ARRAY['#00BBCC','#CCCC00'],20);
insert into Rectangle(position,color,height,width) values(ROW(10,5),ARRAY['#00BBCC','#CCCC00'],20,50);
insert into Rectangle(position,color,height,width) values(ROW(30,-10),ARRAY['#00BBCC','#CCCC00'],20,50);

Si executem diversos Select * from, obtenim a DBeaver:

![Select Figuras](./img/Select_Figura.png){width=90%}
![Select Rectangle](./img/Select_Rectangle.png){width=90%}
![Select Quadrat](./img/Select_Quadrat.png){width=90%}
![Select Circulo](./img/Select_Circulo.png){width=90%}

Com és lògic, és possible pensar que quan seleccionem dades de la taula general (Select * from Figura) apareixeran tots els elements de les subclasses. Si volguéssim seleccionar només aquells que són Figura, podríem fer-ho amb (Select * from ONLY Figura).

Anem a completar l'exemple creant un dibuix amb totes les figures que hem emmagatzemat. Guardarem el dibuix en una nova classe que conté la identificació del dibuix, i guardarem una col·lecció amb els identificadors de les figures que componen el dibuix.

SQL
1
2
3
4
5
6
7
create table Dibujo(
    drawingid serial primary key,
    items int[]
);

insert into Drawing (elements) values(ARRAY[2,4,5,6]);
insert into Drawing (elements) values(ARRAY(select fid from Figura));

Comentar que la selecció dels identificadors de Figura pot ser directa, o seleccionant aquells que volem, mitjançant una consulta incrustada dins del constructor ARRAY. Això es pot fer quan el select retorna una sola columna.

Obtenim els dibuixos que tenim, aquesta col·lecció de figures es mostra. Però si volem descompondre el vector, per accedir a cadascuna de les figures que hi ha, ho podem fer amb la funció unnest.