Sei sulla pagina 1di 8

SQL PER LA DEFINIZIONE DI BASI DI DATI

domenica 24 ottobre 2021 20:13

SQL non è solo un linguaggio id interrogazione (Query Language), ma è anche:


- Un linguaggio per la definizione di basi di dati (Data­definition language (DDL): istruzioni SQL per la
definizione delle basi di dati) CREATE SCHEMA Nome AUTHORIZATION Utente
CREATE TABLE o VIEW, con vincoli
CREAT INDEX
CREATE PROCEDURE
CREATE TRIGGER
- Un linguaggio per stabilire controlli sull'uso dei dati: GRANT (sarebbero i diritti).
- Un linguaggio per modificare i dati

DDL sono quelle istruzioni SQL che io utilizzo per definire la struttura del mio database. Al momento
dell'accensione del computer, io sistema operativo vado a prendere alcune informazioni da bios e le utilizzo
per configurare il mio computer, per gestire le periferiche, ossia tutti i dispositivi che il computer deve gestire
(sistema operativo e insieme di processi → applicazioni che entrano in esecuzione fino a quando si decide che
non servano più).
La RAM è il contenitore di informazione, 32RAM sono random accensable memory (memoria di sovralettura),
la memoria di archiviazione sono sempre bit, ma harddisk è dove o ho una persistenza dei dati.
SSD hard­disk a memoria ad accesso veloce ed è dove io ho una persistenza dei dati, tutto quello che io
archivio poi me lo ritrovo (la vera memoria che contiene informazioni multimediale).
L'harddisk si utilizza per archiviare le informazioni, invece la RAM si utilizza per appoggiare temporaneamente
alcune informazioni, ha caratteristiche di accesso veloci (rispetto all'hard­disk), è un'informazione volatile.
Il sistema operativo è un insieme di processi, che sono algoritmi, applicazioni che entrano in esercizione, vi
rimangono perché lo richiede il sistema operativoni fino a quando il sistema operativo lo ritiene necessario, ad
esempio quando spengo il computer.
Noi dobbiamo capire cosa fanno i DML e cosa fanno i DDL e dove risiedono; i database contengono dati ed
istruzioni (quelle informazioni che noi cerchiamo di archiviare), contiene quelle DDL (informazioni che ho
utilizzato per creare la struttura del database e vengono memorizzate nella parte fisica della persistenza.
Se faccio SELECT mi fa join fra due tabelle, e quindi mi restituisce un'altra tabella che io vedo a video, ma se
non la salvo, spegnendo e accendendo il computer, la SELECT non c'è più perché viene salvata solo nella
memoria RAM. Le DDL sono delle informazioni che io eseguo in un istante, ma che mi genera una struttura
permanente, persistenza dei dati. Sono informazioni di generazione della struttura che creano informazioni
permanenti all'interno della struttura. Replace vuol dire sostituire la sua struttura, magari perché mentre
eseguivo una delle query mi sono dimenticata di aggiungere uno specifico attributo nell'analisi dei requisiti.

Struttura di un database Oracle:

È un insieme di processi, ci sono anche processi che sono attivi sul computer e che stanno usando la RAM.
CPU è la parte di calcolo, usano il disco quindi vuol dire che stanno swappando (togli e metti informazioni dalla
memoria). Se io lancio access, mi trovo un processo, se invece io faccio andare oracle ci sono più processi
perchè architettura è più complessa.
Una parte di informazioni è in grado di dire che ad un certo punto viene generato il mio database, insieme di
file che vengono gestiti da questo sistema complesso, ma questi file che architettura hanno?
Utenti ≡ Schema, perché utente è composto da uno schema e da dei diritti.
Ogetto∅∅ è l'utente che avrà uno schema e dei diritti (quindi ci sarà un superutente quello del database che mi
ha dato diritti a poter fare qualcosa), lo schema è un nome che andrò a dare all'insieme di tabelle che io voglio
creare, è uno spazio fisico che è dedicato al mio oggetto; all'interno ho un insieme di strutture che possiamo
modificare (tabelle ≡ à , viste, indici, procedure …, consfraint che sono i vincoli/associazioni).
Tutto quello studiato a livello progettuale va trasformato in qualcosa di persistente e per farlo dobbiamo
utilizzare le istruzioni.
Utilizzeremo un framework per creare applicazioni che si appoggia già a delle tabelle relazionali che sappiamo
interpretare.
Da wizzard si vede la DDL e si vede l'istruzione vera e propria che è stata eseguita.

Definizione di tabelle
Metto l'elenco degli attributi che mi sono serviti per costruire la tabella. Devo sapere qual è il dominio per
trasformarlo in un tipo di dato, se volevo solo anno dovevo mettere integer, se volevo la data devo tornare
indietro dove ho definito il dominio di quel dato e definire il tipo data.
b è uno spazio per indicare un carattere vuoto, sarebbe b con la sbarra, chiamato "blank". CHAR indica una
stringa in cui posso mettere il carattere che voglio, numero o lettera. CHECK vuol dire che già nel momento
dell'inserimento devo considerare la condizione dopo (è un vincolo).
DEFAULT 'medico', vuol dire che se io non scrivo niente (tipo due virgole), se provo a fare una insert su questa
tabella e non scrivo niente su qualifica, per default quel valore lì assume sempre medico. Se non valorizzo quel
campo di default è 'medico'.
PRIMARY KEY: Determinante che identifica i valori tutti gli altri attributi della non chiave della tabella.

Esempi:
CREATE TABLE Anagrafe
(
Codice CHAR(8) NOT NULL,
Nome CHAR(20),
AnnoNascita INTEGER CHECK (AnnoNascita<2000),
Qualifica CHAR(20) DEFAULT ‘Medico’,
RepartoCodice CHAR(8),
PRIMARY KEY pk_ Anagrafe (Codice),
FOREIGN KEY fk_RepartoDiAppartenenza(RepartoCodice)
REFERENCES Reparto
)

CREATE TABLE FamiliariACarico


(Nome CHAR(20),
AnnoNascita INTEGER,
GradoParentela CHAR(10),
CapoFamiglia CHAR(8)
FOREIGN KEY fk_ FamiliariACarico (CapoFamiglia))

Se non c'è la primary key come quella sopra, magari descrive una tabella con entità debole in cui la primary key
coincida con la foreign key.
Ciò che si crea con CREATE si può eliminare con il comando DROP (distrugge e cancella dal database quella
tabella) o cambiare con il comando ALTER (magari aumentare il campo da 4 a 6 cifre perché ho malvalutato
che potesse aumentare il numero di cifre). Ricorda che DROP è molto pericolosa e quindi va utilizzata con
condizione di causa, in fase di sviluppo la posso usare tranquillamente, è in produzione (dati reali consistenti) è
un vero problema. Delete è sempre meno pericolosa.
Io non posso ridurre le dimensioni del campo perché potrebbe esserci un dato che sia superiore al decremento
dei dati, mentre posso sempre aumentare, far modifica in incremento.

La sintesi della CREATE:


CREATE TABLE Nome
(
Attributo Tipo [ValoreDefault] [VincoloAttributo]
{,Attributo Tipo [Default] [VincoloAttributo]}
{, VincoloTabella}
)
Default := DEFAULT {valore | null | username}

Nuovi attributi si possono aggiungere in secondo momento con la seguente istruzione:

ALTER TABLE Nome ADD COLUMN NuovoAttributo Tipo

Tabelle inizializzate
Vuol dire che nel momento in cui la creo, già le assegno dei valori. Quando si fa installazione nuova, si crea uno
schema, si fa un utente applicativo (gli si dà il nome dello schema) e poi si eseguono tutte le DDL e così creiamo
un contenitore vuoto; spesso, soprattutto sui dizionari, le inserisco già dei valori e posso farlo anche creando
una tabella inizializzata che sia però temporanea mentre lavoro, per lavorarci per più tempo.
La SELECT sotto viene eseguita sulla tabella Anagrafe ma la sto usando per costruire la tabella Medici, perché
c'è istruzione ruolo='Medico', come colonne come minimo avrà 5 campi (incluso infatti il ruolo che è un altro
attributo), 5 campi indicheranno probabilmente tutti i dipendenti. Quindi con la SELECT dopo CREATE
inizializzo una tabella.
CREATE TABLE Nome EspressioneSELECT
Esempio:
CREATE TABLE Medici
SELECT Codice, Nome, Qualifica, Stipendio
FROM Anagrafe
WHERE Ruolo=’Medico’

Tabelle calcolate (viste):


Questa SELECT è la stessa; ma una vista per noi è una finestra sulla tabella (apro una finestra e vedo cosa
contiene quella tabella); eseguo una SELECT che mi restituisce una tabella (potrebbe essere una relazione), con
la quale, tramite una vista (DDL, altro oggetto del database) che ogni volta che proverò a leggere il contenuto
di quella vista, essa eseguirà quella select per me.
Un database funzionale mi dà delle performance, mi permette di scrivere velocemente in maniera strutturata,
se faccio un Join tutti i giorni, eseguire sempre queste SELECT tanto vale creare una vista, che è quindi un
contenitore virtuale.
La vista infatti risiede in memoria RAM ed è molto più performante che accedere sul database, alla memoria
fisica; i database non solo contiene le tabelle, ma ha creato delle viste (che sono DDL) che sono esecuzione di
quella SELECT, io potrei scrivere:
SELECT * FROM Medici;
Non sa se medici è una tabella o vista, ma mi restituisce il contenuto di medici. Le viste sono quelle SELECT che
io eseguo con una altissima periodicità e può tornarmi utile costruire questo strumento vista mediante DDL
che mi rimane permanentemente nel database. In una vista io memorizzo soltanto la struttura, è inutile
copiare anche i dati, perché se devo modificare un dato poi lo devo modificare anche nella tabella medici.
All'interno del mio database modifico l'anagrafe, tutte le viste in automatico sono aggiornate perchè sono una
proiezione di un'informazione reale in base ad una condizione che sono nel WHERE.

CREATE VIEW Nome [(Attributo {, Attributo})] AS EspressioneSELECT


[WITH CHECK OPTION];
Esempio
CREATE VIEW Medici AS
SELECT Codice, Nome, Qualifica, Stipendio
FROM Anagrafe
WHERE Ruolo=’Medico’
Le viste servono anche per:
- Nascondere certe modifiche all'organizzazione logica dei dati (indipendenza logica): se ti mostro una
tabella di 7 campi noi non sappiamo che magari sono diverse tabelle in cui ci sono fatti Join tra le tabelle
e noi non lo sappiamo se non chè abbiamo la DDL (istruzione che ha creato quella vista e da cui
risaliamo). Posso nascondere all'applicazione la struttura della base di dati, se decidessi di cambiare
quella tabella e farla diventare di 3, 5,6 campi e tale modifica non impatta sulla vista la applicazione
continua a funzionare; permettere il funzionamento della applicazioni.
- Offrire visioni diverse degli stessi dati senza ricorrere a duplicazioni: posso mostrare i dati come voglio, è
solo memoria RAM, non sto duplicando i dati.
- Rendere più semplici, o rendere possibili, alcune interrogazioni

Interrogazioni impossibili senza l’uso delle viste


Ad esempio supponiamo di dover trovare il numero medio di medici per reparto. Con V_ io so che stiamo
parlando della vista.

CREATE VIEW V_NumMediciPerReparto (Reparto, NumMedici) //nome dei campi tra parentesi
AS SELECT Reparto, count(*)
FROM Anagrafica
WHERE Qualifica = ‘Medico’
GROUP BY Reparto
SELECT avg(NumMedici) FROM V_NumMediciPerReparto

Con l'ultima riga io sto facendo una media di quel valore prendendolo a partire da una vista e non da tabella.
Questa funziona perché ho creato una vista: "contami i medici di ogni reparto".

NB: I dati quando li definisci con DDL (linguaggio per definire la struttura della base di dati) stanno nella RAM;
quando li salvi, passano al disco e poi puoi fare DML (manipolazione dei dati) sia sui dati della RAM che del
disco però quelli della RAM se chiudi il PC scompaiono.

Vincoli di integrità
Se io inserisco un dato e una tabella in un campo e una foreign key questo vincolo va a controllare se esiste
quel valore nella tabella correlata dove c'è la primary key. Sono importanti perché dimostrano che abbiamo
dato intelligenza al database per controllare la correttezza e consistenza dei dati
Vincoli su attributi
VincoloAttributo: = [NOT NULL [UNIQUE] ] | [CHECK (Condizione) ]
[REFERENCES Tabella [(Attributo {, Attributo})]]

Vincoli su tabella
VincoloTabella:= UNIQUE (Attributo {, Attributo}) | CHECK (Condizione) |
| PRIMARY KEY [Nome] (Attributo {, Attributo}) | FOREIGN KEY [Nome]
(Attributo {, Attributo}) REFERENCES Tabella [(Attributo {, Attributo})]
[ON DELETE {NO ACTION| CASCADE | SET NULL} ]

Esempio
CREATE TABLE Anagrafe
( Codice CHAR(8) NOT NULL,
Nome CHAR(20) NOT NULL,
AnnoNascita INTEGER NOT NULL,
RepartoCodice CHAR(20),
Stipendio FLOAT NOT NULL,
PRIMARY KEY pk_impiegato (Codice),
FOREIGN KEY fk_ RepartoDiAppartenenza (RepartoCodice)
REFERENCES Reparto ON DELETE SET NULL)

Create procedure/function
La funzione restituisce sempre un risultato. Entrambe possono ricevere in ingresso (input) dei parametri,
entrambe eseguono statement, ossia delle istruzioni SQL (nel linguaggio si dice spesso che ho eseguito una
SELECT equindi ho eseguito uno statement); l'istruzione è unica che restituirà un risultato.
CREATE FUNCTION contaStudenti IS //fase dichiarativa e ha una struttura che si ripete
DECLARE
numStudenti INTEGER; //dove dichiaro le variabili, decisi dal programmatore con affianco il
tipo di dato
BEGIN
SELECT COUNT(*) INTO numStudenti
FROM STUDENTI;
RETURN (numStudenti);
END

Quando si parla di variabile si parla di ambiente, che può essere dinamico, statico ecc. Ambiente è spazio di
memoria usato per tenere informazioni.

I trigger
È un altro oggetto del database (database=insieme di processi e file, i processi vengono eseguiti dal computer,
vengono caricati in memoria e rimangono attivi, perché se interrogo il database qualcuno deve rispondere). Un
database è un insieme di processi, ci sono quelli che eseguono delle attività: come creare l'ambiente e tutto
affinché si possa iniziare a lavorare; il database mi dà a disposizioni i suoi oggetti, tra cui i trigger che
rimangono in modo permanente nel database.
Esso è costruito sul paradigma causa­effetto evento­condizione­azione (ECA):
Si verifica una condizione e sulla base di essa dico quali sono le cose da effettuare di conseguenza.

CREATE TRIGGER Nome


PrimaODopoDi Evento {, Evento} ON Tabella [WHEN Condizione]
[Granularità]
Azione
PrimaODopoDi := BEFORE | AFTER
Evento := INSERT | DELETE | UPDATE OF Attributi
Granularità := FOR EACH ROW | FOR EACH STATEMENT

Evento può essere l'inserimento (magari controllare se questo appartiene alla black list oppure no), può essere
anche su una modifica oppure anche su una delete (come per esempio imporre la possibilità di cancellare a
meno che l'infermiere abbia già somministrato il farmaco → condizione)

Esempio
CREATE TRIGGER ControlloStipendio
BEFORE INSERT ON AnagraficaDipendenti //prima dell'INSERT, AnagraficaDipendenti tabella/entità
DECLARE
StipendioMedio FLOAT //variabili dichiarate
BEGIN
SELECT AVG(Stipendio) INTO StipendioMedio
FROM AnagraficaDipendenti
WHERE Reparto = :new.Reparto; //controlla il vecchio valore con il nuovo
IF :new.Stipendio > 2 * StipendioMedio
THEN RAISE_APPL._ERR.(­2061, ‘Stipendio alto’)
END IF;
END;

All'interno del BEGIN END c'è il corpo del trigger, la condizione qual è? Che prima che avvenga INSERT tu svolgi
queste attività: lì dentro di solito o so già che cosa voglio fare oppure se non lo so metto un IF. È una
condizione­azione in base all'evento.
Sicuramente le scriveremo per rispettare i vincoli che abbiamo fatto affinché la nostra tabella sia in DK­NF.
Proprietà essenziale dei trigger: terminazione
Utilità dei trigger
- Trattare vincoli non esprimibili nello schema
- Attivare automaticamente azioni sulla base di dati quando si verificano certe condizioni.
Controllo degli accessi
In un sistema di database, tra i vari processi abbiamo il processo che si occupa di controllare chi ha i diritti per
fare cosa, non tutti possono fare tutto. Anche nella nostra machina virtuale avremo il database e i diritti di
amministratore e quindi quel profilo, l'applicazione che svolgeremo si appoggia ad un secondo utente che
creiamo dentro la base dei dati e i diritti a questo utente vengono dati dall'amministratore. Dare dei diritti
vuol dire che io amministratore consento di accedere a parte della base dati, alle tabelle su cui dovranno
lavorare gli utenti.
Grant sono i diritti e dare i grant vuol dire dare i diritti per accedere, non all'intera base dei dati, ma solo alle
tabelle in cui dobbiamo lavorare (sotto­tabella), diritti di scrittura, lettura, creazione su quella parte.
I diritti sono SELECT, INSERT, DELETE, UPDATE…
Chi crea lo schema della database è l'unico che può fare CREATE, ALTER e DROP. Esiste proprio una
problematica che si chiama Rbac ed è quella che mi dice che sto definendo i ruoli per controllare gli accessi ai
dati. Più ho dettagli di questa informazione e più sono preciso, però potrebbe essere davvero enorme.
Immaginiamoci una tabella con funzioni e profili da una parte e l'altra; e la compileremo in base al fatto che il
medico possa fare una funzione ecc. se ho un dettaglio di questi profili e dettaglio di queste funzioni potrei
avere una tabella davvero enorme e potrei anche avere difficoltà a tenerla aggiornata; tutto questo ci fa capire
come le istruzioni del GRANT siano importantissime, potremmo costruire dei sistemi più o meno
flessibili/potenti oppure rigidi già dalla fase di progettazione.
GRANT si possono eseguire con delle funzioni ricorsive (eseguire quei passi fino a che non si arriva al punto), un
GRANT può essere ricorsivo, ossia io do i diritti di fare creazioni di tabella con opzione ADD IN ossia che questi
(a cui ho dato i diritti) possono dare loro la stessa possibilità a terzi.
La ricorsione ADD IN di poter ridare i diritti è molto pericolosa.

Chi crea una tabella stabilisce i modi in cui altri possono farne uso:
GRANT Privilegi ON Oggetto TO Utenti [ WITH GRANT OPTION ]
Tipi di privilegi/diritti sono:
SELECT: lettura di dati
INSERT [(Attributi)]: inserire record (con valori non nulli per gli attributi)
DELETE: cancellazione di record
UPDATE [(Attributi)]: modificare record (o solo gli attributi)
REFERENCES [(Attributi)]: definire chiavi esterne in altre tabelle che riferiscono gli attributi.
WITH GRANT OPTION: si possono trasferire i privilegi ad altri utenti.

Chi definisce una tabella o una VIEW ottiene automaticamente tutti i privilegi
su di esse, ed è l’unico che può fare un DROP e può autorizzare altri ad usarla con GRANT. Nel caso
di viste, il "creatore" ha i privilegi che ha sulle tabelle usate nella definizione.
Le autorizzazioni si annullano con il comando:
REVOKE [ GRANT OPTION FOR ] Privilegi ON Oggetto FROM Utenti [CASCADE]
Quando si toglie un privilegio ad U, lo si toglie a tutti coloro che lo hanno avuto solo da U

Esempi di Grant
GRANT INSERT, SELECT ON Esami TO Tizio .
GRANT DELETE ON On Esami TO Capo WITH GRANT OPTION
Capo può cancellare record esami ed esso stesso può autorizzare altri a farlo (solo il capo può fare
cancellazione).
GRANT UPDATE (voto) ON Esami TO Sicuro
Sicuro può modificare solo il voto degli esami, però non mi accorgo che ci sono dei cortocircuiti dovuti al fatto
che io ho dato dei diritti a qualcuno che a sua volta li ha ridati e penso di aver costruito un sistema sicuro ma
non è vero,
GRANT SELECT, INSERT ON VistaEsamiInfMedica TO Santarelli, Mangione
Solo loro due possono vedere cosa è fatto sulla lista esami di informatica medica.

Esempio
Qua possiamo vedere come vengono assegnati i diritti. È rappresentato il "Grafo delle autorizzazioni".
L'utente I ha creato la tabella R e innesca la seguente successione di eventi:
- I: GRANT SELECT ON R TO A WITH GRANT OPTION
- A: GRANT SELECT ON R TO B WITH GRANT OPTION
- B: GRANT SELECT ON R TO A WITH GRANT OPTION
- I: GRANT SELECT ON R TO C WITH GRANT OPTION
- C: GRANT SELECT ON R TO B WITH GRANT OPTION

SYS assegna i diritti di lettura ad I con GRANT OPTION e da lì parte poi tutta una serie di catene di
assegnazione. È difficile tenere il controllo dei GRANT, di solito si creano dei macroinsiemi di diritti (già
precompilati), per evitare di ricrearli con il rischio di sbagliare.

Proprietà
- Se un nodo N ha un arco uscente con un privilegio, allora esiste un cammino da SYSTEM a N con ogni arco
etichettato dallo stesso privilegio + WGO
- Effetto del REVOKE, ad es.
I:REVOKE SELECT ON R FROM A CASCADE
- E poi
I:REVOKE SELECT ON R FROM C CASCADE

È stato revocano un diritto, si pensava così di averli tolti anche ad A ma li aveva ricevuti comunque come
possiamo vedere dall'immagine.

Creazione di indici
Supponendo di avere un'insieme di celle di memoria dove dentro ho dei nominativi; un indice potrebbe essere
quello che mi permette di scorrere l'elenco in ordine alfabetico; io faccio la prima INSERT nella tabella; ad
esempio inseriamo PIPPO, QUI e PLUTO, quando inserisco QUI dopo PIPPO si collegano fra i due perché in
ordine alfabetico, ma quando arriva PLUTO, che in realtà è secondo in ordine alfabetico, indice cancella il
legame tra pippo e qui e PIPPO lo farebbe puntare a PLUTO e PLUTO a QUI. Questa creazione di puntatori è
quello che fa la base dei dati con la presenza dell'indice per tenere aggiornato l'indice.
Lo faccio nel momento dell'inserimento perché mi farà guadagnare tantissimo in termini di tempo e quindi di
performance quando andrò a leggere le informazioni.
Un indice mi serve se voglio fare una ricerca sul database e mi dà un tempo di risposta logaritmica (perché
posso dimezzare di volta in volta il numero di dati su cui posso fare la ricerca) e servono ad indicizzare e quindi
formare un insieme/catena di legami tra insieme di informazioni affinché io possa recuperare queste
informazioni in modo performante.
C'è sempre nel database un indice quando io parlo di chiave primaria (perché io uso una chiave per modificare
un record); io avrò un indice per ogni chiave primaria se non ce l'ho è un errore, se non troviamo nessun indice
vuol dire che non è performante. Tutte le chiave primarie o candidate potrebbero avere un indice.
Per la creazione di un indice non c'è un comando standard dell'SQL e quindi ci sono differenze nei vari sistemi.
CREATE INDEX NomeIdx ON Tabella(Attributi)

CREATE INDEX NomeIdx ON Tabella


WITH STRUCTURE = BTREE, KEY = (Attributi)

DROP INDEX NomeIdx

Catalogo dei metadati


Alcuni esempi di tabelle, delle quali si mostrano solo alcuni attributi, sono:
• Tabella delle password:
PASSWORD(username, password)
• Tabella delle basi di dati:
SYSDB(dbname, creator, dbpath, remarks)
• Tabella delle tabelle (type = view or table):
SYSTABLES(name, creator, type, colcount, filename, remarks)

Alcuni esempi di tabelle, delle quali si mostrano solo alcuni attributi, sono:
• Tabella degli attributi:
SYSCOLUMNS(name, tbname, tbcreator, colno, coltype, lenght, default, remarks)
• Tabella degli indici:
SYSINDEXES(name, tbname, creator, uniquerule, colcount)
• e altre ancora sulle viste, vincoli, autorizzazioni, etc. (una decina).

Riepilogo
DDL consente la definizione di tabelle, viste e indici. Le tabelle si possono modificare aggiungendo o togl
iendo attributi e vincoli. Le viste si possono interrogare come ogni altra tabella, ma in generale non con
sentono modifiche dei dati. I comandi GRANT / REVOKE + viste offrono ampie possibilità di controllo degli usi
dei dati. SQL consente di dichiarare molti tipi di vincoli, oltre a quelli fondamentali di chiave e referenziale. O
ltre alle tabelle fanno parte dello schema le procedure e i trigger. La padronanza di tutti questi meccanismi ­­
e di altri che riguardano aspetti fisici, affidabilità, sicurezza ­­ richiede una professionalità specifica (DBA).

NB: il modello logico ci dà delle istruzioni precise su quali saranno le tabelle che noi andremo a costruire.

Potrebbero piacerti anche