SQL (Structured Query Language) è un linguaggio standardizzato per database basati sul modello relazionale (RDBMS) progettato per:
- Creare e modificare schemi di database (DDL - Data Definition Language):
CREATE
,ALTER
,DROP
; - Inserire, modificare e gestire dati memorizzati (DML - Data Manipulation Language):
INSERT
,UPDATE
,DELETE
; - Interrogare i dati memorizzati (DQL - Data Query Language):
SELECT
; - Creare e gestire strumenti di controllo ed accesso ai dati (DCL - Data Control Language):
GRANT
,REVOKE
.
Fonte: SQL - Wikipedia.
IMPORTANTE! Non riassumerò la teoria presentata durante il corso, ma solamente la sintassi SQL collegata.
N.B.: La sintassi presentata segue lo standard originale e non lo standard Oracle!
Simbolo | Significato |
---|---|
[ ... ] |
Elemento opzionale |
< ... > |
Elemento obbligatorio |
/ |
Fine file/Terminazione script |
Operatore | Funzione |
---|---|
= |
Uguaglianza |
<> |
Disuguaglianza |
> |
Maggiore |
< |
Minore |
>= |
Maggiore o uguale |
<= |
Minore o uguale |
.. |
Range di valori |
-- |
Commento su linea singola |
/* */ |
Commento multilinea |
Tipo di dato | Nome |
---|---|
Numerico intero | INTEGER |
Numerico decimale | DECIMAL , REAL , FLOAT |
Carattere, Stringa fissa | CHAR |
Stringa variabile | VARCHAR |
Data | DATE |
Ora | TIME |
Data/Ora | TIMESTAMP , DATETIME |
- Introduzione:
- Definizione dei metadati (
DDL
);- Creazione di un database;
- Domini: tpi personalizzati;
- Creazione di tabelle e vincoli (
CREATE
):- Definizione di record;
- Creare la tabella;
- Esprimere i vincoli (
CONSTRAINT
):
- Modifica di tabelle (
ALTER
); - Eliminazione dei metadati (
DROP
);
- Gestione dei dati (
DML
): - Costruire un'iterrogazione (
DQL
): - Viste (
VIEW
);
La prima operazione utile per interagire con un DBMS è creare un database. Il database conterrà tutte le nostre tabelle che a loro volta conterranno record di dati.
CREATE DATABASE <nomeDatabase>;
E' anche possibile definire dei tipi personalizzati basati su tipi primitivi:
CREATE DOMAIN <nomeDominio> AS <TipoPrimitivo>
[DEFAULT <valore>]
[CHECK <expressione booleana | CONSTRAINT>]
Es.:
CREATE DOMAIN voto AS UNSIGNED INTEGER(2)
DEFAULT 18
CHECK (
VALUE >= 0 AND VALUE <= 31 -- Il prof. Peron utilizza il nome del dominio anziche la parola chiave 'VALUE': voto >= 0 AND voto <= 31
)
Supponiamo che 0
sia per gli astenuti/assenti e 31
per la lode.
Un altro elemento fondamentale per la creazione della propria base di dati sono le tabelle. Esse conterranno i record di dati.
Un record (di dati) è una struttura organizzata di informazioni, ovvero un insieme di informazioni con caratteristiche comuni e con un dato ordine.
Un esempio generico di record può essere:
<ID> <nome> <cognome> <dataDiNascita>
Ora, tutti i record facenti parte di questa collezione (collection) seguiranno l'ordine sopra presentato. Es.:
1 Mario Rossi 01/01/1980
2 John Doe 12/03/1974
...
In SQL, la sintassi per definire quindi la struttura di contenimento, ovvero la tabella, è la seguente:
CREATE TABLE <nomeTabella> (
<nomeCampo> <tipoCampo> [NULL | NOT NULL] [DEFAULT <valore>] [PRIMARY KEY | UNIQUE] [,
<nomeCampo> <tipoCampo> [NULL | NOT NULL] [DEFAULT <valore>] [PRIMARY KEY | UNIQUE] [,
...
]] [,
CONSTRAINT ...
]
)
Oppure è possibile creare una tabella utilizzando una query di selezione:
CREATE TABLE <nomeNuovaTabella> AS (SELECT ...)
ATTENZIONE! Oltre a selezionare la struttura, questa forma seleziona anche i dati che rispondono ad essa, per ovviare a questo problema è possibile indicare la clausola WHERE 1 = 0
oppure eseguire un TRUNCATE TABLE <nomeNuovaTabella>
per eliminare i dati presenti.
E' inoltre possibile definire dei vincoli sulla seguente tabella. Un vincolo è una condizione che il campo deve rispettare per poter essere considerato valido.
I vincoli più semplici e comuni sono i vincoli di integrità intrarelazionale.
Essi vengono definiti anche CONSTRAINT
e la sintassi generica per dichiararne uno in una tabella è la seguente:
-- Dopo aver dichiarato i campi...
CONSTRAINT <nomeVincolo> <definizione> [ENABLE | DISABLE]
E' importante dire che il nome di un vincolo è univoco in tutto il database!
Indica un campo (o un insieme di campi) che hanno lo scopo di identificare univocamente un record della tabella. Hanno valore su tutta la tabella.
In fase di creazione esistono i seguenti modi per dichiarare questo vincolo:
- Vicino alla dichiarazione della campo:
<nomeCampo> <tipoCampo> PRIMARY KEY
; - Alla fine della dichiarazione dei campi:
PRIMARY KEY (<campo>)
; - Creando una
CONSTRAINT <nomeVincolo> PRIMARY KEY (<campo> [, <campo>, ...])
;
Indica un campo (o un insieme di campi) che possono avere valori unici ed univoci in tutta la tabella; vieta la possibilità di dati duplicati sui campi specificati. Hanno valore su tutta la tabella.
In fase di creazione esistono i seguenti modi per dichiarare questo vincolo:
- Vicino alla dichiarazione della campo:
<nomeCampo> <tipoCampo> UNIQUE
; - Alla fine della dichiarazione dei campi:
UNIQUE (<campo>)
; - Creando una
CONSTRAINT <nomeVincolo> UNIQUE (<campo> [, <campo>, ...])
;
Indica un limite di valore che il campo (o un insieme di campi) può assumere.
In fase di creazione esistono i seguenti modi per dichiarare questo vincolo:
- Vicino alla dichiarazione della campo:
<nomeCampo> <tipoCampo> CHECK (<condizione>)
; - Alla fine della dichiarazione dei campi:
CHECK (<condizione>)
; - Creando una
CONSTRAINT <nomeVincolo> CHECK (<condizione>)
;
Esistono anche vincoli di integrità interrelazionali. Il più utilizzato è quello di integrità referenziale, definito FOREIGN KEY
.
Una foreign key è un campo che fa riferimento ad un campo (principalmente una primary key) di un'altra tabella ed è utilizzato per rappresentare un'associazione fra le due.
Anche vincolo può essere definito in fase creazionale nelle seguenti tre forme:
- Vicino alla dichiarazione del campo:
<nomeCampo> <tipoCampo> FOREIGN KEY REFERENCES <nomeTabella>(<campo>) [ON UPDATE <RESTICT | NO ACTION | CASCADE | SET NULL>] [ON DELETE <RESTICT | NO ACTION | CASCADE | SET NULL>]
; - Alla fine della dichiarazione dei campi:
FOREIGN KEY (<campo>) REFERENCES <nomeTabella>(<campo>) [ON UPDATE <RESTICT | NO ACTION | CASCADE | SET NULL>] [ON DELETE <RESTICT | NO ACTION | CASCADE | SET NULL>]
; - Creando una
CONSTRAINT <nomeVincolo> FOREIGN KEY (<campo>) REFERENCES <nomeTabella>(<campo>) [ON UPDATE <RESTICT | NO ACTION | CASCADE | SET NULL>] [ON DELETE <RESTICT | NO ACTION | CASCADE | SET NULL>]
;
Come definito dalla sintassi, per una foreign key è anche possibile specifare che operazione compiere in caso di modifica o perdita di informazioni.
Adesso verranno specificate le varie definizioni per le clausole ON UPDATE
e ON DELETE
:
NO ACTION
oppureRESTRICT
(solo per MySQL): Default. Non compie nessuna azione, lasciando i dati invariati;CASCADE
: Elimina oppure aggiorna automaticamente il valore del campo facente riferimento al dato record (consigliato perON UPDATE
);SET NULL
: Imposta aNULL
il valore del campo facente riferimento al dato record (consigliato perON DELETE
);
Es.:
Supponiamo di avere le tabelle roles
e employees
. Ogni dipendente ha un solo ruolo. Un ruolo piò essere svolto da più dipendenti.
L'associazione presentata è una 1 a molti, quindi la chiave primaria di roles
viene ereditata da employees
(vedi l'esempio generale di questa sezione).
Essendo la relazione definita nel seguente modo: ON UPDATE CASCADE ON DELETE NO SET NULL
, otterremo i seguenti risultati:
- Se modificassi l'ID di un ruolo (indicato tra parentesi), per esempio
Capo Reparto (2)
⇒Capo Reparto (10)
, essendoci la clausola aON UPDATE CASCADE
, tutti i dipendenti col ruolo di capo reparto verranno aggiornati facendo riferimento al record aggiornato; - Se eliminassi il ruolo con ID pari a
3
, essendoci la clausola aON DELETE SET NULL
, tutti i dipendenti col ruolo con ID3
verranno aggiornati avendo valoreNULL
;
Tabella employees
originale:
id | name | surname | role_id | salary |
---|---|---|---|---|
1 | Mario | Rossi | 2 | 3000 |
2 | Ugo | Bianchi | 3 | 1750 |
2 | Matteo | Salvini | 5 | 800 |
2 | Rocco | Lunghi | 2 | 3000 |
2 | Bruno | Corti | 2 | 3000 |
Tabella employees
aggiornata:
id | name | surname | role_id | salary |
---|---|---|---|---|
1 | Mario | Rossi | 10 | 3000 |
2 | Ugo | Bianchi | NULL |
1750 |
2 | Matteo | Salvini | 5 | 800 |
2 | Rocco | Lunghi | 10 | 3000 |
2 | Bruno | Corti | 10 | 3000 |
Spesso un campo può anche dipendere da un valore di un altro campo presente in un'altra tabella, per esprimere questa condizione è possibile definire un'asserzione:
CREATE ASSERTION <nomeAsserzione> CHECK (<condizione>)
Esempio che racchiude tutte le nozioni appena espresse:
CREATE DATABASE company_db;
CREATE TABLE roles (
id INTEGER(2) PRIMARY KEY.
title VARCHAR(20) NOT NULL,
min_salary DECIMAL(7, 2) NULL DEFAULT 250,
max_salary DECIMAL(7, 2) NOT NULL,
CONSTRAINT min_salary_chk CHECK (min_salary >= 250),
CONSTRAINT max_salary_chk CHECK (max_salary >= 30000)
);
CREATE TABLE employees (
id INTEGER(10) PRIMARY KEY,
name VARCHAR(32) NOT NULL,
surname VARCHAR(32) NOT NULL,
role_id INTEGER(2),
salary DECIMAL(7, 2) NOT NULL,
FOREIGN KEY (role_id) REFERENCES roles(id)
ON UPDATE CASCADE
ON DELETE SET NULL
);
CREATE ASSERTION emp_salary_chk CHECK (
NOT EXISTS (
SELECT *
FROM employees AS emp
JOIN roles AS role ON emp.role_id = role.id
WHERE emp.salary NOT BETWEEN role.min_salary AND role.min_salary
)
)
IMPORTANTE! Le asserzioni non sono quasi più supportate a fini effettivi, ma il prof. Peron ne ha molta considerazione.
La modifica di metadati è principalmente applicata alle sole tabelle.
E' infatti possibile:
- Modificare le colonne di una tabella;
- Modificare(*) i vincoli di una tabella;
La sintassi generica è:
ALTER TABLE <nomeTabella> <operazione>
ALTER TABLE <nomeTabella>
ADD COLUMN <nomeCampo> <tipoCampo> [NULL | NOT NULL] [DEFAULT <valore>]
ALTER TABLE <nomeTabella>
MODIFY COLUMN <nomeCampo> <tipoCampo> [NULL | NOT NULL] [DEFAULT <valore>]
ALTER TABLE <nomeTabella>
DROP COLUMN <nomeCampo>
ALTER TABLE <nomeTabella>
ADD CONSTRAINT <nomeConstraint> <definizione>
ALTER TABLE <nomeTabella>
DROP CONSTRAINT <nomeConstraint>
Secondo lo standard del linguaggio, non è possibile modificare direttamente un vincolo, ma può essere eliminato e ricreato.
ALTER TABLE employees
DROP CONSTRAINT <nomeConstraint>;
ALTER TABLE employees
ADD CONSTRAINT <nomeConstraint> <definizioneAggiornato>
Il DROP
è la funzione di eliminazione ed utilizza la seguente sintassi:
DROP <tipoMetadato> <nome>
Per esempio:
DROP TABLE employees
ATTENZIONE! E' importante far notare che all'eliminazione di un database o di una tabella, i dati in essi contenuti verrano persi!
Una volta creata la giusta struttura è giunto il momento di gestire i nostri dati. E' ovvio dire che i dati che inseriremo/aggiorneremo/elimineremo devo rispettare i vincoli definiti!
Per inserire un un record in una tabella è possibile usare l'istruzione INSERT
:
INSERT INTO <nomeTabella>(<campo> [, <campo>[, ...]])
VALUES (<valore> [, <valore>[, ...]])
La corrispondenza tra colonne indicate e valori è posizionale.
Es.:
INSERT INTO roles(id, title, min_salary, max_salary)
VALUES (1, 'Manager', 1500, 3500);
INSERT INTO roles(id, title, min_salary, max_salary)
VALUES (2, 'Capo Reparto', 1200, 3000);
E' anche possibile inserire in una tabella dati ottenuti da una SELECT
.
Es.:
INSERT INTO roles(id, title, min_salary, max_salary)
SELECT * FROM old_roles WHERE id > 2
Lo statemente di modifica è probabilmente uno dei più pericolosi in quanto la sintassi sembra essere stata scritta per favorire la modifica di tutti i record anziche quella di uno specifico.
UPDATE <nomeTabella>
SET <campo> = <nuovoValore> [,
<campo> = <nuovoValore> [, ...]]
[WHERE <condizione>]
Es.:
UPDATE SET employees
SET salary = 3000
I salari di tutti i dipendenti saranno impostati a 3000
.
E' quindi FONDAMENTALE fare attenzione alla clausola WHERE
:
UPDATE SET employees
SET salary = 3000
WHERE id = 3
Modifica del salario a 3000
del solo dipendente con ID pari a 3
.
E' anche possibile definire un aggiornamento sul vecchio valore:
UPDATE SET employees
SET salary = salary + 1000
WHERE id = 3
Lo statement di eliminazione è probabilmente il più compromettente in quanto elimina definitivamente uno o più record. Può anche eliminare tutti i record, ma la tabella rimarrà.
La sintassi è la seguente:
DELETE [*] FROM <nomeTabella>
[WHERE <condizione>]
Come per l'update, anche qui bisogna fare molta attenzione alla clausola WHERE
!
Es.:
DELETE FROM employees
Elimina tutti i dipendenti.
DELETE FROM employees
WHERE id = 3
Elimina solo il dipendete con ID pari a 3
.
Un'interrogazione è basata principalmente dalla clausola SELECT
.
La sintassi completa è la seguente:
SELECT [ALL | DISTINCT] <* | <campo> [AS <nuovoNome>] | <campoAggregato> [AS <nuovoNome>] [, [campo [AS <nuovoNome> ...]]>>
FROM <nomeTabella> [AS <alias>]
[[INNER | LEFT | RIGHT | FULL OUTER] JOIN <nomeTabella> [AS <alias>] ON <condizioneAssociazione>]
[[[INNER | LEFT | RIGHT | FULL OUTER] JOIN <nomeTabella> [AS <alias>] ON <condizioneAssociazione>] ...]
[WHERE <condizione | condizioneSuSubquery>]
[GROUP BY <campo> [, <campo>[, ...]]] [HAVING <condizioneRagruppamento>]
[ORDER BY <campo> [, <campo>[, ...]]] [ASC | DESC]
IS NULL
: Verifica se il campo èNULL
;IS NOT NULL
: Verifica se il campo NON èNULL
;
EXISTS
: Verifica che la riga o il campo esistano in una tabella (indicata da subquery);NOT EXISTS
: Verifica che la riga o il campo NON esistano in una tabella (indicata da subquery);
LIKE
: Verifica se il campo ha un valore che risponde alla stringa passata;NOT LIKE
: rifica se il campo NON ha un valore che risponde alla stringa passata;
IN
: Verifica che il campo ha un valore presente nella lista passata;NOT IN
: Verifica che il campo NON ha un valore presente nella lista passata;
BETWEEN
: Verifica che il campo ha un valore compreso nel range indicato;NOT BETWEEN
: Verifica che il campo NON ha un valore compreso nel range indicato;
Le viste sono selezioni a cui viene associato un nome, inoltre esse sono principalmente virtuali, possono anche essere memorizzate (ciò comporta solo vantaggi di efficienza).
Su una vista possono essere eseguite solo SELECT
.
Quindi non possono essere utilizzate le seguenti operazioni: INSERT
, UPDATE
e DELETE
.
E' quindi anche possibile: creare (CREATE
), eliminare (DROP
).
La sintassi per la creazione di una vista è la seguente:
CREATE VIEW <nomeVista>[(<col1>[, <col2>, ...])] AS
SELECT ...;
La corrispondenza tra colonne della vista e colonne della query di selezione è posizionale.
Es.:
CREATE VIEW most_payed_emp_by_dep AS (
SELECT emp.id, emp.name, emp.surname
FROM empolyees AS emp
)