diff --git a/4-5-schema-logico.md b/4-5-schema-logico.md index e796ed4..7e5a66d 100644 --- a/4-5-schema-logico.md +++ b/4-5-schema-logico.md @@ -71,6 +71,7 @@ Di seguito si riporta l'intero schema logico dopo aver effettuato tutte le trasf - _Provenienza_ ### Recensione (libro) + - **ID** → [Elemento (libro)](#elemento-libro) - Commento - Valutazione diff --git a/5-3-creazione-tabelle.md b/5-3-creazione-tabelle.md index b12a5de..891ba8f 100644 --- a/5-3-creazione-tabelle.md +++ b/5-3-creazione-tabelle.md @@ -35,18 +35,21 @@ Si riportano solo le tabelle con qualche particolarità; le tabelle per la quale ```sql CREATE TABLE public.audiolibro_edizione ( - isbn integer NOT NULL, + isbn character(13) NOT NULL, titolo character varying NOT NULL, durata interval, immagine bytea, - relativa_a integer NOT NULL + relativa_a integer NOT NULL, + CONSTRAINT durata_check CHECK ((date_part('epoch'::text, durata) >= (0)::double precision)) ); 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. +La durata delle edizioni degli audiolibri è di tipo _interval_, che rappresenta un intervallo di tempo con la precisione di centesimi di secondo; l'immagine dell'audiolibro ha invece tipo _bytea_, e sarà salvata nel database come un blob binario di dati. + +Inoltre, la durata è dotata di un _CHECK_ che impedisce che essa sia minore di 0, convertendo l'_interval_ in secondi e controllando che essi siano maggiori di 0. ### `audiolibro_recensione` @@ -55,17 +58,23 @@ CREATE TABLE public.audiolibro_recensione ( id bigint NOT NULL, commento text NOT NULL, valutazione smallint NOT NULL, - data timestamp without time zone NOT NULL, + data timestamp without time zone DEFAULT now() 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); + +ALTER TABLE ONLY public.audiolibro_recensione + ADD CONSTRAINT id FOREIGN KEY (id) REFERENCES public.audiolibro_elemento(id); ``` -La valutazione delle recensioni deve essere obbligatoriamente tra 0 e 100: a tale scopo, è stato introdotto un _CHECK_ sulla tabella. +Per minimizzare valori nulli, si è tradotta l'associazione binaria 1 a 1 `relativa a` in due tabelle separate, usando in una delle due una chiave esterna come chiave primaria. +Si notino dunque i due _CONSTRAINT_ `audiolibro_recensione_pkey` e `id`. -La data di pubblicazione è rappresentata da un _timestamp_. +La valutazione delle recensioni deve essere obbligatoriamente tra 0 e 100: a tale scopo, è stato introdotto un _CHECK_ sulla tabella che verifichi questa condizione. + +La data di pubblicazione è rappresentata da un _timestamp_, tipo che rappresenta un istante specifico di tempo con precisione fino ai secondi. ### `film` @@ -75,19 +84,17 @@ CREATE TABLE public.film ( titolo character varying NOT NULL, sinossi text, locandina bytea, - durata integer, - CONSTRAINT film_durata_check CHECK ((durata >= 0)) + durata interval, + CONSTRAINT durata_check CHECK ((date_part('epoch'::text, durata) > (0)::double precision)) ); 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. +Nella tabella `film` compare nuovamente il _CHECK_ utilizzato nelle `audiolibro_edizioni` per la durata, e lo stesso tipo _bytea_ per la locandina. -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_. +Si può notare nella tabella compare il tipo _char(34)_: essendo gli EIDR sempre lunghi 34 caratteri, si è scelto si minimizzare lo spazio di archiviazione utilizzato rendendo il campo a lunghezza non variabile. ### `film_correlazioni` @@ -133,7 +140,7 @@ 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. +L'associazione ternaria è stata realizzata con un'altra **tabella ponte**, avente una **chiave primaria composta** e tre chiavi esterne separate. ### `elemento_id_seq` @@ -148,9 +155,9 @@ CREATE SEQUENCE public.elemento_id_seq 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`. +Per realizzare ciò si è creata una _SEQUENCE_ unica che viene poi usata da tutte le tabelle `*_elemento` per generare i nuovi id. -### `gioco_stato` e `gioco_provenienza` +### Stato e provenienza ```sql CREATE TYPE public.gioco_provenienza AS ENUM ( @@ -171,7 +178,7 @@ CREATE TYPE public.gioco_stato AS ENUM ( ); ``` -Gli stati e le provenienze degli elementi sono state realizzate tramite _ENUM_ contenenti tutte le possibili opzioni selezionabili dall'utente. +Gli stati e le provenienze dei vari elementi sono state realizzate creando _ENUM_ contenenti tutte le possibili opzioni selezionabili dall'utente, e utilizzandoli come tipo delle relative colonne; in questo modo, si impedisce l'immissione di valori non validi nelle colonne. ### `gioco_elemento` @@ -195,53 +202,46 @@ ALTER TABLE ONLY public.gioco_elemento CREATE FUNCTION public.update_n_giochi() RETURNS trigger LANGUAGE plpgsql - AS $$ - BEGIN + 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 + +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. +Le colonne `stato` e `provenienza` utilizzano il relativo _ENUM_ creato in precedenza. -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. +La colonna `id` ha un valore di _DEFAULT_ particolare: `nextval('public.elemento_id_seq'::regclass)`. +Ciò significa che, se non viene specificato un `id` durante un inserimento, alla riga verrà assegnato automaticamente il valore corrente della _SEQUENCE_ `public.elemento_id_seq` descritta in precedenza, aumentandone inoltre il valore di 1. -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. +Nella tabella è presente anche un _TRIGGER_, che incrementa o decrementa il conteggio degli elementi dell'utente a cui essi appartengono quando uno o più elementi vengono inseriti o rimossi. ### `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 FUNCTION public.is_numeric(text character varying) RETURNS boolean + LANGUAGE plpgsql STRICT + 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, @@ -260,7 +260,9 @@ 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. +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. + +In particolare, per quest'ultimo, è stata creata una funzione di utilità `is_numeric`, che verifica che tutti i caratteri di una stringa siano numerici: questa funzione viene poi usata per controllare che tutti i caratteri dell'ISBN siano numeri, permettendo però anche una `X` in ultima posizione. ### `utente` @@ -285,4 +287,5 @@ La password, essendo un [hash](https://it.wikipedia.org/wiki/Funzione_di_hash), 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. +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. +Queste colonne saranno poi incrementate dai trigger presenti nelle quattro tabelle `*_elemento`. diff --git a/5-database.sql b/5-database.sql index 35aed68..72f2d34 100644 --- a/5-database.sql +++ b/5-database.sql @@ -224,7 +224,8 @@ CREATE TABLE public.audiolibro_edizione ( titolo character varying NOT NULL, durata interval, immagine bytea, - relativa_a integer NOT NULL + relativa_a integer NOT NULL, + CONSTRAINT durata_check CHECK ((date_part('epoch'::text, durata) >= (0)::double precision)) ); @@ -307,8 +308,8 @@ CREATE TABLE public.film ( titolo character varying NOT NULL, sinossi text, locandina bytea, - durata integer, - CONSTRAINT film_durata_check CHECK ((durata >= 0)) + durata interval, + CONSTRAINT durata_check CHECK ((date_part('epoch'::text, durata) >= (0)::double precision)) ); @@ -894,13 +895,13 @@ COPY public.audiolibro_recensione (id, commento, valutazione, data) FROM stdin; -- COPY public.film (eidr, titolo, sinossi, locandina, durata) FROM stdin; -10.5240/29E6-2D4B-B704-2B69-441F-8 Titanic \N \N 195 +10.5240/29E6-2D4B-B704-2B69-441F-8 Titanic \N \N 03:15:00 10.5240/E030-107B-C08A-BF93-AC79-B The Lord of the Rings: The Fellowship of the Ring \N \N \N 10.5240/598E-D2AE-1B7D-67EB-3F75-6 The Lord of the Rings: The Two Towers \N \N \N 10.5240/B07E-E305-8057-CD37-0887-E The Lord of the Rings: The Return of the King \N \N \N -10.5240/0EF3-54F9-2642-0B49-6829-R Inception \N \N 148 +10.5240/0EF3-54F9-2642-0B49-6829-R Inception \N \N 02:28:00 10.5240/3CB5-4D3B-BC77-8A4E-78D4-9 La vita è bella \N \N \N -10.5240/E73C-4D4C-4F90-781B-B62F-K Shining \N \N 146 +10.5240/E73C-4D4C-4F90-781B-B62F-K Shining \N \N 02:26:00 10.5240/09A3-1F6E-3538-DF46-5C6F-I Back to the Future \N \N \N 10.5240/5DA5-C386-2911-7E2B-1782-L Back to the Future Part II \N \N \N \. diff --git a/6-operazioni.md b/6-operazioni.md index b4d8950..145da37 100644 --- a/6-operazioni.md +++ b/6-operazioni.md @@ -73,7 +73,7 @@ FROM gioco_recensione gr JOIN gioco_elemento gel on gr.id = gel.id JOIN gioco_edizione ge on gel.istanza_di = ge.id JOIN gioco g on ge.relativa_a = g.id -WHERE g = $id_gioco; +WHERE g.id = $id_gioco; ``` ## Visualizzazione di tutte le edizioni di un dato editore diff --git a/README.md b/README.md index 8f9c77e..534ab34 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ Un database per un ipotetico sito web di gestione libreria multimediale Realizzato in collaborazione tra [@Steffo99](https://github.com/Steffo99/) e [@Cookie-CHR](https://github.com/Cookie-CHR) per l'esame di [Basi di Dati](http://personale.unimore.it/rubrica/contenutiad/rmartoglia/2019/58030/N0/N0/9999) dell'[Unimore](https://www.unimore.it/). -> Questo progetto non è ancora terminato. Molte cose possono ancora variare! - ## Specifiche Le specifiche di questo progetto sono disponibili nel file [`spec.pdf`](0-spec.pdf). @@ -22,9 +20,7 @@ Le specifiche di questo progetto sono disponibili nel file [`spec.pdf`](0-spec.p 4. **Progettazione logica** 1. [Eliminazione delle gerarchie](4-1-eliminazione-gerarchie.md) 2. [Eliminazione delle chiavi esterne](4-2-eliminazione-chiavi-esterne.md) - 3. [Trasformazione degli attributi composti](4-3-trasformazione-degli-attributi-composti.md) - 4. [Dati derivati](4-4-dati-derivati.md) 5. [Schema logico](4-5-schema-logico.md) 6. [Verifica di normalizzazione](4-6-normalizzazione.md)