Finora abbiamo lavorato con singole tabelle. Tuttavia, nei database reali, i dati sono spesso distribuiti in più tabelle collegate tra loro. Ad esempio, in un e-commerce potremmo avere una tabella per gli utenti e una per gli ordini. Per combinare questi dati e ottenere informazioni complete, utilizziamo i JOIN.
Con Node.js e MySQL possiamo eseguire query JOIN per recuperare dati da più tabelle contemporaneamente, creando relazioni tra le informazioni.
Prima di utilizzare i JOIN, è importante capire come le tabelle si collegano tra loro. Le tabelle sono collegate tramite chiavi:
PRIMARY KEY): identifica in modo univoco ogni riga di una tabella. Di solito è una colonna chiamata id.FOREIGN KEY): è una colonna che fa riferimento alla chiave primaria di un'altra tabella, creando una relazione tra le tabelle.Ad esempio, immaginiamo due tabelle:
Tabella utenti:
| id | nome | |
| 1 | Mario | [email protected] |
| 2 | Luigi | [email protected] |
Tabella ordini:
| id | utente_id | prodotto | prezzo |
| 1 | 1 | Laptop | 999.99 |
| 2 | 1 | Mouse | 29.99 |
| 3 | 2 | Tastiera | 79.99 |
Nella tabella ordini, la colonna utente_id è una chiave esterna che fa riferimento all'id della tabella utenti. Questo ci permette di sapere quale utente ha effettuato un ordine.
L'INNER JOIN è il tipo di JOIN più comune. Restituisce solo le righe in cui la condizione di JOIN è soddisfatta in entrambe le tabelle.
Vediamo come utilizzarlo in Node.js per ottenere tutti gli ordini con i dati degli utenti:
const sql = `SELECT ordini.id, ordini.prodotto, ordini.prezzo, utenti.nome, utenti.email
FROM ordini
INNER JOIN utenti ON ordini.utente_id = utenti.id;`;
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
});Nell'esempio:
SELECT per specificare quali colonne vogliamo recuperare, come abbiamo visto nella lezione su SELECT. Per evitare ambiguità, scriviamo nomeTabella.nomeColonna.FROM ordini indica la tabella principale da cui partiamo.INNER JOIN utenti specifica la tabella da unire.ON ordini.utente_id = utenti.id definisce la condizione di JOIN: colleghiamo le righe dove utente_id in ordini corrisponde a id in utenti.Il risultato sarà un array contenente tutti gli ordini con i relativi dati dell'utente:
[
{ id: 1, prodotto: 'Laptop', prezzo: 999.99, nome: 'Mario', email: '[email protected]' },
{ id: 2, prodotto: 'Mouse', prezzo: 29.99, nome: 'Mario', email: '[email protected]' },
{ id: 3, prodotto: 'Tastiera', prezzo: 79.99, nome: 'Luigi', email: '[email protected]' }
]Quando lavoriamo con JOIN, le query possono diventare lunghe. Possiamo rinominare le tabelle con nomi più brevi o significativi per rendere il codice più leggibile. Utilizziamo la sintassi nome_tabella AS nuovo_nome.
const sql = `SELECT o.id, o.prodotto, o.prezzo, u.nome, u.email
FROM ordini AS o
INNER JOIN utenti AS u ON o.utente_id = u.id;`;In questo caso, o è l'alias per ordini e u per utenti. Il risultato sarà identico, ma la query è più compatta.
Il LEFT JOIN restituisce tutte le righe della tabella di sinistra (quella specificata in FROM) e le righe corrispondenti della tabella di destra. Se non c'è corrispondenza, i valori della tabella di destra saranno NULL.
Questo è utile quando vogliamo vedere tutti gli utenti, anche quelli che non hanno effettuato ordini:
const sql = `SELECT u.nome, u.email, o.prodotto, o.prezzo
FROM utenti AS u
LEFT JOIN ordini AS o ON u.id = o.utente_id;`;
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
});Se l'utente "Giacomo" (id:3) esiste nella tabella utenti ma non ha ordini, il risultato includerà:
{ nome: 'Giacomo', email: '[email protected]', prodotto: null, prezzo: null }Il RIGHT JOIN è l'opposto del LEFT JOIN: restituisce tutte le righe della tabella di destra e le corrispondenze della tabella di sinistra. È meno comune del LEFT JOIN:
const sql = `SELECT u.nome, u.email, o.prodotto, o.prezzo
FROM ordini AS o
RIGHT JOIN utenti AS u ON o.utente_id = u.id;`;Il risultato sarà simile al LEFT JOIN precedente. In questo caso, utenti è la tabella di destra, quindi otterremo tutti gli utenti anche se non hanno ordini.
Possiamo utilizzare WHERE per filtrare i risultati di un JOIN, proprio come con le query normali:
const sql = `SELECT o.id, o.prodotto, o.prezzo, u.nome
FROM ordini AS o
INNER JOIN utenti AS u ON o.utente_id = u.id
WHERE o.prezzo > 50;`;
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
});Questo restituirà solo gli ordini con prezzo superiore a 50, insieme ai dati dell'utente.
Possiamo anche utilizzare ORDER BY per ordinare i risultati, come abbiamo visto nella lezione sull'ordinamento dei dati:
const sql = `SELECT o.id, o.prodotto, o.prezzo, u.nome
FROM ordini AS o
INNER JOIN utenti AS u ON o.utente_id = u.id
ORDER BY o.prezzo DESC;`;I risultati saranno ordinati dal prezzo più alto al più basso.
Come sempre, quando utilizziamo valori provenienti dall'utente, dobbiamo usare parametri preparati per evitare SQL injection (come abbiamo visto meglio nella lezione sull'inserimento dei dati nel database):
const nomeUtente = "Mario";
const sql = `SELECT o.id, o.prodotto, o.prezzo, u.nome
FROM ordini AS o
INNER JOIN utenti AS u ON o.utente_id = u.id
WHERE u.nome = ?;`;
con.query(sql, [nomeUtente], function (err, result) {
if (err) throw err;
console.log(result);
});Il placeholder ? viene sostituito automaticamente con il valore di nomeUtente in modo sicuro.
Vediamo un esempio completo che combina Express con MySQL per mostrare gli ordini di un utente:
const express = require("express");
const mysql = require("mysql");
const app = express();
const con = mysql.createConnection({
host: "localhost",
user: "root",
password: "la_tua_password",
database: "esempio"
});
con.connect(function (err) {
if (err) throw err;
console.log("Connesso al database");
});
app.get("/ordini/:utente_id", function (req, res) {
const utenteId = req.params.utente_id;
const sql = `SELECT o.id, o.prodotto, o.prezzo, u.nome, u.email
FROM ordini AS o
INNER JOIN utenti AS u ON o.utente_id = u.id
WHERE u.id = ?;`;
con.query(sql, [utenteId], function (err, result) {
if (err) {
res.send("Errore nel recupero degli ordini");
return;
}
if (result.length === 0) {
res.send("Nessun ordine trovato per questo utente");
return;
}
res.json(result);
});
});
app.listen(3000);Visitando http://localhost:3000/ordini/1, si otterranno tutti gli ordini dell'utente con id 1 in formato JSON.