Nelle lezioni precedenti abbiamo visto come interrogare i database attraverso SQL, utilizzando istruzioni come SELECT, WHERE, JOIN, GROUP BY e le altre funzionalità del linguaggio.
Finora abbiamo lavorato principalmente con dati già presenti nelle tabelle. Ora vediamo come creare e modificare la struttura di un database.
Quando creiamo una tabella, per ogni colonna dobbiamo specificare un tipo di dato. Il tipo di dato stabilisce quali valori possono essere memorizzati nella colonna e, in alcuni casi, come MySQL deve memorizzarli.
Tra i tipi di dato più utilizzati troviamo:
| Tipo | Descrizione |
INT | Numeri interi |
BIGINT | Numeri interi molto grandi |
VARCHAR | Stringhe di lunghezza variabile |
TEXT | Testi lunghi |
DECIMAL | Numeri decimali con precisione esatta |
DATE | Date |
DATETIME | Data e ore |
JSON | Dati in formato JSON |
ENUM | Valori scelti da un insieme predefinito |
BOOLEAN | Valori booleani, ovvero true e false |
Vediamo i tipi di dato in dettaglio.
INT viene utilizzato per memorizzare numeri interi.
eta INTINT è il tipo predefinito per le colonne id.
BIGINT salva numeri interi, ma permette di rappresentare numeri molto più grandi rispetto a INT.
Può essere utile, ad esempio, quando dobbiamo memorizzare identificativi o contatori che potrebbero superare il limite di INT (di 4 byte, circa 4 miliardi di numeri).
id BIGINTVARCHAR viene utilizzato per memorizzare stringhe di lunghezza variabile. Tra parentesi, è necessario indicare la lunghezza massima che avranno le stringhe salvate nella colonna.
Ad esempio, per una stringa di massimo 100 caratteri:
nome VARCHAR(100)Il tipo TEXT è pensato per contenere testi più lunghi. Può essere utilizzato per descrizioni, articoli, commenti e altri contenuti testuali di dimensioni maggiori.
descrizione TEXTDECIMAL viene utilizzato per numeri decimali che richiedono precisione esatta, come prezzi e importi monetari. È necessario indicare il numero massimo di cifre complessive e il numero di cifre decimali.
In questo caso 10 indica il numero massimo di cifre complessive, mentre 2 indica quante cifre possono essere presenti dopo la virgola:
prezzo DECIMAL(10, 2)DATE permette di memorizzare una data, generalmente nel formato YYYY-MM-DD.
data_nascita DATEDATETIME permette di memorizzare data e ora, di solito nel formato YYYY-MM-DD HH:MM:SS.
creato_il DATETIMEIl tipo JSON permette di memorizzare dati strutturati nel formato JSON.
impostazioni JSONNella colonna impostazioni dell'esempio, possiamo salvare dati di questo tipo:
{"tema": "dark", "notifiche": true}ENUM permette di definire un insieme di valori consentiti. Ad esempio:
stato ENUM('attivo', 'inattivo')La colonna stato dell'esempio potrà quindi contenere uno dei valori definiti, attivo o inattivo.
È importante scegliere ENUM con criterio: è utile quando l'insieme dei valori è stabile e limitato, mentre per valori che possono cambiare frequentemente può essere più appropriata un tipo di dato diverso, come VARCHAR.
BOOLEAN permette di memorizzare i valori boolean true o false nella colonna.
attivo BOOLEANPer creare una nuova tabella utilizziamo l'istruzione CREATE TABLE, che ha la seguente sintassi:
CREATE TABLE nome_tabella (
colonna1 TIPO,
colonna2 TIPO,
colonna3 TIPO
);Per esempio, possiamo creare una tabella utenti:
CREATE TABLE utenti (
id INT,
nome VARCHAR(100),
email VARCHAR(255),
data_nascita DATE
);Eseguendo la query, abbiamo creato una tabella con quattro colonne:
id, di tipo INT;nome, di tipo VARCHAR;email, di tipo VARCHAR;data_nascita, di tipo DATE.Ogni definizione di colonna contiene quindi almeno il nome della colonna e tipo di dato che può salvare.
Possiamo aggiungere anche diversi vincoli, che stabiliscono ulteriori regole sui dati.
Una delle colonne più importanti di una tabella è la chiave primaria, definita con PRIMARY KEY. La chiave primaria identifica in modo univoco ogni riga della tabella.
Ad esempio:
CREATE TABLE utenti (
id INT PRIMARY KEY,
nome VARCHAR(100),
email VARCHAR(255)
);In questo esempio, id è la chiave primaria. Quando una colonna è una chiave primaria:
id.NULL.PRIMARY KEY.Quando utilizziamo un identificativo numerico, può essere utile fare in modo che MySQL generi automaticamente un nuovo valore per ogni riga.
Possiamo utilizzare AUTO_INCREMENT:
CREATE TABLE utenti (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100),
email VARCHAR(255)
);Ora possiamo inserire un utente senza specificare manualmente id:
INSERT INTO utenti (nome, email)
VALUES ('Marco', '[email protected]');MySQL assegnerà automaticamente un valore alla colonna id. Ad esempio, se sono già state inserite 20 righe alla tabella, al nuovo id verrà assegnato 21.
Per impostazione predefinita, una colonna può generalmente contenere NULL. Se invece vogliamo obbligare una colonna ad avere un valore, possiamo utilizzare NOT NULL.
Ad esempio, potrebbe essere necessario che un account abbia sempre un'email:
CREATE TABLE utenti (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
nome VARCHAR(100)
);La seguente istruzione di inserimento, quindi, non sarà valida, perché non viene fornito un valore per email:
INSERT INTO utenti (nome) VALUES ('Andrea');Nota: approfondiremo INSERT nella lezione sugli inserimenti dei dati.
Il vincolo UNIQUE permette di impedire che una colonna contenga valori duplicati.
Per esempio, l'indirizzo email di un utente dovrebbe essere univoco:
CREATE TABLE utenti (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) UNIQUE,
nome VARCHAR(100)
);Non sarà quindi possibile inserire due righe con lo stesso valore di email.
DEFAULT permette di specificare un valore predefinito per una colonna. Se eseguiamo una query di inserimento senza specificare un valore per una determinata colonna, MySQL utilizzerà il valore predefinito della colonna.
Ad esempio:
CREATE TABLE utenti (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
attivo BOOLEAN DEFAULT TRUE
);Se inseriamo un utente senza specificare attivo, verrà scelto il valore true.
CHECK permette di stabilire una condizione che deve essere rispettata dal valore inserito.
Per esempio, vogliamo che prezzo sia sempre positivo:
CREATE TABLE prodotti (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
prezzo DECIMAL(10, 2) CHECK (prezzo >= 0)
);Possiamo utilizzare più vincoli sulla stessa colonna.
Per esempio:
CREATE TABLE utenti (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
attivo BOOLEAN NOT NULL DEFAULT TRUE
);In questo esempio:
id è la chiave primaria e viene generato automaticamente.nome è obbligatorio;email è obbligatoria e deve essere univoca.attivo è obbligatorio e, se non specificato, assume il valore TRUE.Nelle lezioni sull'integrità referenziale abbiamo visto come collegare più tabelle attraverso colonne correlate.
Per rappresentare questo collegamento a livello di struttura del database possiamo utilizzare una foreign key, o chiave esterna.
Supponiamo di avere:
utenti con colonne id e nome.ordini con colonne id, cliente_id e totale. La colonna cliente_id deve contenere l'id di un degli utenti in utenti.Possiamo creare le tabelle così:
CREATE TABLE utenti (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL
);
CREATE TABLE ordini (
id INT AUTO_INCREMENT PRIMARY KEY,
cliente_id INT,
totale DECIMAL(10, 2),
FOREIGN KEY (cliente_id) REFERENCES utenti(id)
);La sintassi FOREIGN KEY (cliente_id) REFERENCES utenti(id) stabilisce che cliente_id fa riferimento alla colonna id della tabella utenti. In questo modo MySQL può verificare l'integrità referenziale della relazione.
Quando utilizziamo una foreign key, possiamo stabilire cosa deve accadere alle righe collegate quando viene eliminato o modificato il valore della tabella principale.
Possiamo farlo con ON DELETE e ON UPDATE.
Ad esempio:
CREATE TABLE ordini (
id INT AUTO_INCREMENT PRIMARY KEY,
cliente_id INT,
totale DECIMAL(10, 2),
FOREIGN KEY (cliente_id)
REFERENCES utenti(id)
ON DELETE CASCADE
ON UPDATE CASCADE
);CASCADE propaga l'operazione alla tabella collegata. Con ON DELETE CASCADE se viene eliminato un utente, vengono eliminati automaticamente anche gli ordini associati. Con ON UPDATE CASCADE una modifica alla chiave referenziata viene copiata anche sulla foreign key.
Si possono anche utilizzare:
ON DELETE SET NULL, quando viene eliminata la riga principale, la foreign key delle righe collegate viene impostata a NULL.ON DELETE RESTRICT, impedisce l'operazione quando esistono righe collegate. Ad esempio, se un utente ha ancora degli ordini associati, MySQL impedirà la sua eliminazione.Dopo aver creato una tabella, possiamo aver bisogno di modificarne la struttura. Per farlo utilizziamo ALTER TABLE. Possiamo utilizzare le seguenti clausole:
| Tipo | Descrizione |
ADD | Aggiunge una nuova colonna. |
MODIFY | Modifica una colonna esistente. |
DROP | Elimina una colonna esistente insieme a tutti i dati contenuti al suo interno. |
Vediamoli in dettaglio.
Per aggiungere una nuova colonna a una tabella esistente, usiamo ADD.
ALTER TABLE utenti
ADD telefono VARCHAR(20);La tabella avrà quindi una nuova colonna telefono.
Possiamo anche aggiungere un vincolo o una proprietà:
ALTER TABLE utenti
ADD data_registrazione DATETIME DEFAULT CURRENT_TIMESTAMP;Possiamo modificare il tipo o altre caratteristiche di una colonna utilizzando MODIFY. Per esempio:
ALTER TABLE utenti
MODIFY nome VARCHAR(150) NOT NULL;In questo caso la lunghezza massima di nome passa da quella precedente a 150 caratteri e la colonna viene impostata come NOT NULL.
Quando utilizziamo MODIFY, è importante specificare la definizione che vogliamo mantenere per la colonna, comprese eventuali caratteristiche come NOT NULL o DEFAULT.
Per eliminare una colonna utilizziamo DROP COLUMN seguito dal nome della colonna, ad esempio:
ALTER TABLE utenti
DROP COLUMN telefono;La colonna telefono verrà rimossa dalla struttura della tabella, insieme ai dati che conteneva.
Se vogliamo eliminare un'intera tabella, possiamo utilizzare DROP TABLE, seguito dal nome della tabella. In questo modo, viene eliminata la struttura della tabella, le righe e i dati in essa contenuti, i relativi vincoli e indici definiti sulla tabella.
DROP TABLE utenti;Se vogliamo eliminare tutte le righe mantenendo la struttura della tabella, possiamo utilizzare la sintassi di TRUNCATE TABLE. Ad esempio:
TRUNCATE TABLE utenti;Dopo l'operazione, la tabella continuerà a esistere e manterrà colonne, indici e vincoli, ma sarà vuota.