mirror of
https://github.com/Steffo99/alexandria.git
synced 2024-11-21 21:34:19 +00:00
288 lines
10 KiB
Markdown
288 lines
10 KiB
Markdown
# Creazione tabelle
|
|
|
|
Dopo aver creato il database, il secondo passo della progettazione fisica è stato quello di convertire lo schema logico in un database Postgres.
|
|
|
|
In generale:
|
|
|
|
- Le **entità** sono diventate _TABLES_ (tabelle);
|
|
- Gli **attributi opzionali** sono diventati _COLUMNS_ (colonne);
|
|
- Gli **attributi obbligatori** sono diventati _COLUMNS_ con il vincolo _NOT NULL_;
|
|
- Le **chiavi primarie** sono state implementate come _PRIMARY KEYS_ (chiavi primarie);
|
|
- Le **chiavi esterne** sono state implementate come _FOREIGN KEYS_ (chiavi esterne);
|
|
- Le **chiavi surrogate** sono state implementate come _PRIMARY KEYS_ autoincrementate tramite _SEQUENCES_ (sequenze);
|
|
- I **dati derivati** sono stati implementati come _COLUMNS_ aventi dei _TRIGGER_ che le aggiornino.
|
|
|
|
## Schema dei nomi delle tabelle
|
|
|
|
Tutte le tabelle sono state istanziate con il nome che le corrispondenti entità avevano nello schema logico, sostituendo tutte le lettere maiuscole con **lettere minuscole** `a-z`, spazi con **underscore** `_` e rimuovendo le parentesi con il loro contenuto.
|
|
|
|
Inoltre, a tutte le tabelle tranne `utente` è stato dato un nome prefissato da `libro_`, `audiolibro_`, `film_` e `gioco_` per indicare la categoria a cui le entità appartenevano nello schema logico.
|
|
|
|
### Esempi
|
|
|
|
| Entità | Tabella |
|
|
|--------|---------|
|
|
| [`Utente`](4-5-schema-logico.md#utente) | `utente` |
|
|
| [`Libro`](4-5-schema-logico.md#libro) | `libro` |
|
|
| [`Edizione (libro)`](4-5-schema-logico.md#edizione-libro) | `libro_edizione` |
|
|
| [`Cast`](4-5-schema-logico.md#cast) | `film_cast` |
|
|
|
|
## Creazione tabelle
|
|
|
|
Si riportano solo le tabelle con qualche particolarità; le tabelle per la quale la conversione è banale sono omesse da questo file (ma non dal file [`5-database.sql`](5-database.sql)).
|
|
|
|
### `audiolibro_edizione`
|
|
|
|
```sql
|
|
CREATE TABLE public.audiolibro_edizione (
|
|
isbn integer NOT NULL,
|
|
titolo character varying NOT NULL,
|
|
durata interval,
|
|
immagine bytea,
|
|
relativa_a integer NOT NULL
|
|
);
|
|
|
|
ALTER TABLE ONLY public.audiolibro_edizione
|
|
ADD CONSTRAINT audiolibro_edizione_pkey PRIMARY KEY (isbn);
|
|
```
|
|
|
|
L'immagine relativa all'audiolibro è archiviata nella base di dati come un blob binario di dati.
|
|
|
|
### `audiolibro_recensione`
|
|
|
|
```sql
|
|
CREATE TABLE public.audiolibro_recensione (
|
|
id bigint NOT NULL,
|
|
commento text NOT NULL,
|
|
valutazione smallint NOT NULL,
|
|
data timestamp without time zone NOT NULL,
|
|
CONSTRAINT audiolibro_recensione_valutazione_check CHECK (((valutazione >= 0) AND (valutazione <= 100)))
|
|
);
|
|
|
|
ALTER TABLE ONLY public.audiolibro_recensione
|
|
ADD CONSTRAINT audiolibro_recensione_pkey PRIMARY KEY (id);
|
|
```
|
|
|
|
La valutazione delle recensioni deve essere obbligatoriamente tra 0 e 100: a tale scopo, è stato introdotto un _CHECK_ sulla tabella.
|
|
|
|
La data di pubblicazione è rappresentata da un _timestamp_.
|
|
|
|
### `film`
|
|
|
|
```sql
|
|
CREATE TABLE public.film (
|
|
eidr character(34) NOT NULL,
|
|
titolo character varying NOT NULL,
|
|
sinossi text,
|
|
locandina bytea,
|
|
durata integer,
|
|
CONSTRAINT film_durata_check CHECK ((durata >= 0))
|
|
);
|
|
|
|
ALTER TABLE ONLY public.film
|
|
ADD CONSTRAINT film_pkey PRIMARY KEY (eidr);
|
|
```
|
|
|
|
I film hanno un _CHECK_ che impedisce alla loro durata di essere minore di 0 minuti, nel caso essa sia definita.
|
|
|
|
Il loro `eidr` è una stringa di lunghezza costante _char_, in quanto gli EIDR sono sempre lunghi 34 caratteri.
|
|
|
|
Inoltre, come per gli audiolibri, la loro locandina è immagazzinata nel database come _bytea_.
|
|
|
|
### `film_correlazioni`
|
|
|
|
```sql
|
|
CREATE TABLE public.film_correlazioni (
|
|
eidr_1 character(34) NOT NULL,
|
|
eidr_2 character(34) NOT NULL
|
|
);
|
|
|
|
ALTER TABLE ONLY public.film_correlazioni
|
|
ADD CONSTRAINT film_correlazioni_pkey PRIMARY KEY (eidr_1, eidr_2);
|
|
|
|
ALTER TABLE ONLY public.film_correlazioni
|
|
ADD CONSTRAINT eidr_1 FOREIGN KEY (eidr_1) REFERENCES public.film(eidr);
|
|
|
|
ALTER TABLE ONLY public.film_correlazioni
|
|
ADD CONSTRAINT eidr_2 FOREIGN KEY (eidr_2) REFERENCES public.film(eidr);
|
|
```
|
|
|
|
L'autoassociazione delle correlazioni è stata implementata attraverso una **tabella ponte** che collega due film attraverso i loro `eidr`.
|
|
|
|
`eidr_1` ed `eidr_2` sono due chiavi esterne separate, e insieme formano la **chiave primaria composta** della tabella.
|
|
|
|
### `film_vi_ha_preso_parte`
|
|
|
|
```sql
|
|
CREATE TABLE public.film_vi_ha_preso_parte (
|
|
eidr character(34) NOT NULL,
|
|
id_cast integer NOT NULL,
|
|
id_ruolo integer NOT NULL
|
|
);
|
|
|
|
ALTER TABLE ONLY public.film_vi_ha_preso_parte
|
|
ADD CONSTRAINT film_vi_ha_preso_parte_pkey PRIMARY KEY (eidr, id_cast, id_ruolo);
|
|
|
|
ALTER TABLE ONLY public.film_vi_ha_preso_parte
|
|
ADD CONSTRAINT eidr FOREIGN KEY (eidr) REFERENCES public.film(eidr);
|
|
|
|
ALTER TABLE ONLY public.film_vi_ha_preso_parte
|
|
ADD CONSTRAINT id_cast FOREIGN KEY (id_cast) REFERENCES public.film_cast(id);
|
|
|
|
ALTER TABLE ONLY public.film_vi_ha_preso_parte
|
|
ADD CONSTRAINT id_ruolo FOREIGN KEY (id_ruolo) REFERENCES public.film_ruolo(id);
|
|
```
|
|
|
|
L'associazione ternaria è stata realizzata con una **tabella ponte**, con una **chiave primaria composta** e tre chiavi esterne separate.
|
|
|
|
### `elemento_id_seq`
|
|
|
|
```sql
|
|
CREATE SEQUENCE public.elemento_id_seq
|
|
START WITH 1
|
|
INCREMENT BY 1
|
|
NO MINVALUE
|
|
NO MAXVALUE
|
|
CACHE 1;
|
|
```
|
|
|
|
Si è deciso di rendere **univoci** gli `id` di **tutti gli elementi**, qualsiasi fosse il loro tipo.
|
|
|
|
Per realizzare l'unicità si è creata una unica _SEQUENCE_, che viene usata da tutte le tabelle `*_elemento`.
|
|
|
|
### `gioco_stato` e `gioco_provenienza`
|
|
|
|
```sql
|
|
CREATE TYPE public.gioco_provenienza AS ENUM (
|
|
'GRATUITO',
|
|
'ACQUISTATO',
|
|
'IN_ABBONAMENTO',
|
|
'PRESO_IN_PRESTITO',
|
|
'NON_PIU_POSSEDUTO',
|
|
'ALTRO'
|
|
);
|
|
|
|
CREATE TYPE public.gioco_stato AS ENUM (
|
|
'DA_INIZIARE',
|
|
'INIZIATO',
|
|
'FINITO',
|
|
'COMPLETATO',
|
|
'NON_APPLICABILE'
|
|
);
|
|
```
|
|
|
|
Gli stati e le provenienze degli elementi sono state realizzate tramite _ENUM_ contenenti tutte le possibili opzioni selezionabili dall'utente.
|
|
|
|
### `gioco_elemento`
|
|
|
|
```sql
|
|
CREATE TABLE public.gioco_elemento (
|
|
id bigint DEFAULT nextval('public.elemento_id_seq'::regclass) NOT NULL,
|
|
stato public.gioco_stato,
|
|
provenienza public.gioco_provenienza,
|
|
istanza_di integer NOT NULL,
|
|
appartiene_a character varying NOT NULL
|
|
);
|
|
|
|
ALTER TABLE ONLY public.gioco_elemento
|
|
ADD CONSTRAINT gioco_elemento_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE ONLY public.gioco_elemento
|
|
ADD CONSTRAINT appartiene_a FOREIGN KEY (appartiene_a) REFERENCES public.utente(username);
|
|
|
|
ALTER TABLE ONLY public.gioco_elemento
|
|
ADD CONSTRAINT istanza_di FOREIGN KEY (istanza_di) REFERENCES public.gioco_edizione(id);
|
|
|
|
CREATE FUNCTION public.update_n_giochi() RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
BEGIN
|
|
IF (TG_OP = 'DELETE') THEN
|
|
UPDATE utente
|
|
SET gioco_elementi_posseduti = gioco_elementi_posseduti - 1
|
|
FROM gioco_elemento
|
|
WHERE utente.username = old.appartiene_a;
|
|
RETURN old;
|
|
ELSIF (TG_OP = 'INSERT') THEN
|
|
UPDATE utente
|
|
SET gioco_elementi_posseduti = gioco_elementi_posseduti + 1
|
|
FROM gioco_elemento
|
|
WHERE utente.username = new.appartiene_a;
|
|
RETURN new;
|
|
END IF;
|
|
END;
|
|
$$;
|
|
|
|
CREATE TRIGGER numero_giochi_trigger
|
|
BEFORE INSERT OR DELETE
|
|
ON public.gioco_elemento
|
|
FOR EACH ROW EXECUTE PROCEDURE public.update_n_giochi();
|
|
```
|
|
|
|
Le tabelle degli elementi hanno due colonne `stato` e `provenienza` di tipo `*_stato` e `*_provenienza` rispettivamente: sono gli _ENUM_ creati in precedenza, che impediscono che vengano inseriti valori non consentiti nella tabella.
|
|
|
|
La colonna `id` invece ha un valore di _DEFAULT_ particolare: `nextval('public.elemento_id_seq'::regclass)`.
|
|
Significa che, se non viene specificato un `id` durante un _INSERT_, alla riga verrà assegnato automaticamente il valore corrente della _SEQUENCE_, e il valore della sequenza sarà aumentato, garantendo l'**unicità** degli `id` anche attraverso tabelle diverse.
|
|
|
|
Inoltre, nella tabella è presente un _TRIGGER_, che incrementa o decrementa il conteggio dei giochi di un utente quando egli rispettivamente crea o elimina nuovi elementi.
|
|
|
|
### `libro_edizione`
|
|
|
|
```sql
|
|
create function is_numeric(text character varying) returns boolean
|
|
strict
|
|
language plpgsql
|
|
as
|
|
$$
|
|
DECLARE x NUMERIC;
|
|
BEGIN
|
|
x = $1::NUMERIC;
|
|
RETURN TRUE;
|
|
EXCEPTION WHEN others THEN
|
|
RETURN FALSE;
|
|
END;
|
|
$$;
|
|
|
|
CREATE TABLE public.libro_edizione (
|
|
isbn character(13) NOT NULL,
|
|
titolo_edizione character varying NOT NULL,
|
|
pagine integer,
|
|
copertina bytea,
|
|
relativa_a integer NOT NULL,
|
|
CONSTRAINT libro_edizione_isbn_check CHECK ((public.is_numeric(("substring"((isbn)::text, 1, 12))::character varying) AND (public.is_numeric(("right"((isbn)::text, 1))::character varying) OR ("right"((isbn)::text, 1) ~~ '%X'::text)))),
|
|
CONSTRAINT libro_edizione_pagine_check CHECK ((pagine >= 0))
|
|
);
|
|
|
|
ALTER TABLE ONLY public.libro_edizione
|
|
ADD CONSTRAINT libro_edizione_pkey PRIMARY KEY (isbn);
|
|
|
|
ALTER TABLE ONLY public.libro_edizione
|
|
ADD CONSTRAINT relativa_a FOREIGN KEY (relativa_a) REFERENCES public.libro(id);
|
|
```
|
|
|
|
La tabella delle edizioni di un libro include due _CHECK_: uno che controlla che le pagine, se specificate, siano un numero positivo, e un'altro che controlla che gli ISBN siano in un formato valido, assicurandosi che tutti i caratteri siano numerici e permettendo anche una `X` sull'ultimo.
|
|
|
|
### `utente`
|
|
|
|
```sql
|
|
CREATE TABLE public.utente (
|
|
username character varying NOT NULL,
|
|
password bytea NOT NULL,
|
|
email character varying,
|
|
is_admin boolean DEFAULT false NOT NULL,
|
|
is_banned boolean DEFAULT false NOT NULL,
|
|
libro_elementi_posseduti integer DEFAULT 0 NOT NULL,
|
|
audiolibro_elementi_posseduti integer DEFAULT 0 NOT NULL,
|
|
film_elementi_posseduti integer DEFAULT 0 NOT NULL,
|
|
gioco_elementi_posseduti integer DEFAULT 0 NOT NULL
|
|
);
|
|
|
|
ALTER TABLE ONLY public.utente
|
|
ADD CONSTRAINT username PRIMARY KEY (username);
|
|
```
|
|
|
|
La password, essendo un [hash](https://it.wikipedia.org/wiki/Funzione_di_hash), è rappresentata come un dato binario (_bytea_).
|
|
|
|
Le colonne `is_admin` e `is_banned` hanno un valore di default di _false_, in quanto alla creazione gli utenti non saranno amministratori o bannati.
|
|
|
|
Le colonne `*_elementi_posseduti` sono i dati derivati che rappresentano quanti elementi di ogni tipo possiede un dato utente: per i nuovi utenti, questo valore sarà 0.
|