/* ===================================================================== SE VOLETE FARE IL DB DA ZERO (CONSUGLIATO) Eseguite PRIMA tutto questo blocco per creare lo schema iniziale. ===================================================================== */ PRAGMA foreign_keys = ON; -- attiva i vincoli di chiave esterna in SQLite /* Prima cancelliamo eventuali tabelle preesistenti con gli stessi nomi, così tutti partono da uno schema identico ed evitiamo errori. */ DROP TABLE IF EXISTS Terapia; DROP TABLE IF EXISTS Paziente; DROP TABLE IF EXISTS Farmaci; DROP TABLE IF EXISTS CartellaClinica; DROP TABLE IF EXISTS Referto; /* ===================================================================== CREAZIONE SCHEMA CONSIGLIATO PER GLI ESERCIZI ===================================================================== */ /* Tabella dei referti */ CREATE TABLE Referto ( ID_referto INTEGER PRIMARY KEY, Data NUMERIC, -- formato consigliato: 'YYYY-MM-DD' Conclusione TEXT ); /* Cartella clinica: collega esami e referti */ CREATE TABLE CartellaClinica ( CodiceEsame INTEGER PRIMARY KEY, Data NUMERIC, -- 'YYYY-MM-DD' PercorsoDatoMedico TEXT, ID_referto2 INTEGER, FOREIGN KEY (ID_referto2) REFERENCES Referto(ID_referto) ); /* Tabella dei pazienti: Aggiungiamo SUBITO Nome e Sesso per evitare di modificarla dopo. */ CREATE TABLE Paziente ( CF TEXT PRIMARY KEY, -- Codice Fiscale Cognome TEXT NOT NULL, Nome TEXT NOT NULL, Sesso TEXT, -- 'M', 'F', 'X', ecc. Data_Nascita NUMERIC NOT NULL, -- 'YYYY-MM-DD' o 'YYYY-MM-DD HH:MM:SS' Via TEXT, Num_civ TEXT, Citta TEXT, Stato TEXT, CodiceEsame INTEGER, FOREIGN KEY (CodiceEsame) REFERENCES CartellaClinica(CodiceEsame) ); /* Tabella dei farmaci */ CREATE TABLE Farmaci ( CodiceFarmaco INTEGER PRIMARY KEY AUTOINCREMENT, PrincipioAttivo TEXT ); /* Tabella delle terapie: collega Paziente + Farmaco, con date di inizio/fine. */ CREATE TABLE Terapia ( ID_terapia INTEGER PRIMARY KEY AUTOINCREMENT, CF_paziente TEXT NOT NULL, CodiceFarmaco INTEGER NOT NULL, Data_inizio NUMERIC NOT NULL, -- 'YYYY-MM-DD' Data_fine NUMERIC, -- 'YYYY-MM-DD' Dose TEXT, -- es. "5 mg/die" FOREIGN KEY (CF_paziente) REFERENCES Paziente(CF), FOREIGN KEY (CodiceFarmaco) REFERENCES Farmaci(CodiceFarmaco) ); /* ===================================================================== 1. INSERIMENTO DI DATI (INSERT) – DATE E DATETIME IN SQLITE ===================================================================== */ /* SQLite NON ha un tipo DATE o DATETIME "vero". Consigliato: salvare le date come stringhe di testo in formato ISO: - Solo data: 'YYYY-MM-DD' - Data + ora: 'YYYY-MM-DD HH:MM:SS' - Data + ora + fuso: 'YYYY-MM-DD HH:MM:SS+NN:NN' Anche se il tipo della colonna è NUMERIC, SQLite accetta stringhe e le funzioni date(), datetime(), julianday() funzionano correttamente. */ /* Inseriamo alcuni referti di esempio */ INSERT INTO Referto (ID_referto, Data, Conclusione) VALUES (1, '2018-05-10', 'Controllo di routine nella norma'), (2, '2019-11-20', 'Riacutizzazione ipertensione'), (3, '2014-03-15', 'Controllo Coagulazione'); /* Cartelle cliniche */ INSERT INTO CartellaClinica (CodiceEsame, Data, PercorsoDatoMedico, ID_referto2) VALUES (100, '2018-05-10', '/dati/rossi_2018_05_10.pdf', 1), (101, '2019-11-20', '/dati/nunziatella_2019_11_20.pdf', 2), (102, '2014-03-15', '/dati/verdi_2014_03_15.pdf', 3); /* Inserimento pazienti Esempio di uso di date in formato 'YYYY-MM-DD' */ INSERT INTO Paziente (CF, Cognome, Nome, Sesso, Data_Nascita, Via, Num_civ, Citta, Stato, CodiceEsame) VALUES ('RSSMRA80A01F205X', 'Rossi', 'Mario', 'M', '1980-01-10', 'Via Roma', '10', 'Napoli', 'Italia', 100), ('VRDLGI90B15H501Y', 'Verdi', 'Luigi', 'M', '1990-02-15', 'Via Milano', '5', 'Milano', 'Italia', 102), ('BNCLRA85C30H501Z', 'Bianchi', 'Laura', 'F', '1985-03-30', 'Via Firenze', '22', 'Firenze', 'Italia', NULL), ('NNZNTL70D20H501W', 'Nunziatella', 'Paolo', 'M', '1970-04-20', 'Via Torino', '7', 'Torino', 'Italia', 101), ('NNZNTL95E05H501K', 'Nunziatella', 'Chiara', 'F', '1995-05-05', 'Via Torino', '7', 'Torino', 'Italia', NULL), ('MRTFNC60F10H501Q', 'Martini', 'Francesca', 'F', '1960-06-10', 'Via Venezia', '12', 'Venezia', 'Italia', NULL); /* Inserimento farmaci, compreso il warfarin */ INSERT INTO Farmaci (PrincipioAttivo) VALUES ('warfarin'), ('aspirina'), ('paracetamolo'), ('metformina'), ('atorvastatina'); /* Controlliamo i codici assegnati automaticamente (per curiosità) */ -- SELECT * FROM Farmaci; /* Inseriamo alcune terapie Notare il formato della data: 'YYYY-MM-DD' */ INSERT INTO Terapia (CF_paziente, CodiceFarmaco, Data_inizio, Data_fine, Dose) VALUES -- Terapie con warfarin per vari pazienti ('NNZNTL70D20H501W', 1, '2014-01-10', '2014-12-20', '5 mg/die'), ('NNZNTL70D20H501W', 1, '2018-01-15', '2019-03-10', '3 mg/die'), ('RSSMRA80A01F205X', 1, '2019-02-01', '2019-12-15', '4 mg/die'), -- Terapie con altri farmaci ('VRDLGI90B15H501Y', 2, '2019-01-05', '2019-06-30', '100 mg/die'), ('BNCLRA85C30H501Z', 3, '2017-09-01', '2020-01-01', '1 g/die'), ('MRTFNC60F10H501Q', 4, '2013-05-01', '2014-02-01', '500 mg/die'); /* Esempi di inserimento usando l’ORA CORRENTE (solo a scopo didattico, da NON eseguire se volete dati fissi): INSERT INTO Paziente (..., Data_Nascita, ...) VALUES (..., date('now', '-30 years'), ...); date('now') -> data odierna datetime('now') -> data + ora correnti time('now') -> solo orario corrente */ /* ===================================================================== 2. QUERY DI BASE: SELECT, WHERE, AS, ORDER BY, LIMIT ===================================================================== */ -- 2.1 Selezionare tutti i pazienti (tutte le colonne, tutte le righe) SELECT * FROM Paziente; -- 2.2 Selezionare solo alcune colonne SELECT CF, Cognome, Nome, Data_Nascita FROM Paziente; -- 2.3 Uso di WHERE: pazienti di una certa città SELECT CF, Cognome, Nome, Citta FROM Paziente WHERE Citta = 'Torino'; -- 2.4 Uso di WHERE con più condizioni (AND, OR) SELECT CF, Cognome, Nome, Sesso, Citta FROM Paziente WHERE Citta = 'Torino' AND Sesso = 'F'; -- 2.5 Alias di colonna con AS SELECT CF AS CodiceFiscale, Cognome AS Cognome_Paziente, Nome AS Nome_Paziente FROM Paziente; -- 2.6 Ordinamento con ORDER BY (ASC, DESC) SELECT Cognome, Nome, Data_Nascita FROM Paziente ORDER BY Cognome ASC, Nome ASC; -- ASC è il default -- Ordinamento decrescente per data di nascita (dal più giovane al più vecchio) SELECT Cognome, Nome, Data_Nascita FROM Paziente ORDER BY Data_Nascita DESC; -- 2.7 LIMIT per mostrare solo le prime N righe (es. prime 3) SELECT Cognome, Nome, Data_Nascita FROM Paziente ORDER BY Cognome LIMIT 3; /* ===================================================================== 3. OPERATORI, DATE(), DATETIME(), JULIANDAY(), CALCOLO ETÀ ===================================================================== */ /* -------- 3.1 Esempi di operatori di confronto -------- = uguale <> diverso > maggiore >= maggiore o uguale < minore <= minore o uguale */ -- Pazienti nati dopo il 1 gennaio 1985 SELECT Cognome, Nome, Data_Nascita FROM Paziente WHERE Data_Nascita > '1985-01-01'; -- Pazienti NON nati nel 1990 (semplificando per anno con strftime) SELECT Cognome, Nome, Data_Nascita FROM Paziente WHERE strftime('%Y', Data_Nascita) <> '1990'; /* -------- 3.2 Funzioni su date in SQLite -------- date(...) -> restituisce solo la parte di data in formato 'YYYY-MM-DD' datetime(...) -> restituisce data + ora 'YYYY-MM-DD HH:MM:SS' time(...) -> solo l'ora 'HH:MM:SS' julianday(...) -> numero reale di "giorni giuliani" (utile per differenze) Esempio: date('now'), datetime('now'), time('now') */ -- Data odierna (secondo il sistema) SELECT date('now') AS Data_oggi; -- Data e ora correnti SELECT datetime('now') AS DataOra_oggi; -- Solo ora corrente SELECT time('now') AS Ora_oggi; -- Usare date() sulla Data_Nascita del paziente SELECT CF, Cognome, Nome, date(Data_Nascita) AS Solo_data, time(Data_Nascita) AS Solo_ora, -- se la Data_Nascita ha anche l'ora datetime(Data_Nascita) AS Data_e_ora FROM Paziente; /* -------- 3.3 JULIANDAY: cos'è e a cosa serve -------- julianday('data') restituisce un numero reale corrispondente ai giorni (con parte decimale) trascorsi da una certa data di riferimento (sistema giuliano). Per noi è utile perché: differenza_in_giorni = julianday(data_fine) - julianday(data_inizio) Esempio: calcolare l'età di un paziente: (julianday('now') - julianday(Data_Nascita)) / 365.25 */ -- Calcolo dell'età approssimata di ogni paziente (in anni interi) SELECT CF, Cognome, Nome, CAST((julianday('now') - julianday(Data_Nascita)) / 365.25 AS INTEGER) AS Eta FROM Paziente; -- Calcolo della durata (in giorni) di ciascuna terapia SELECT ID_terapia, CF_paziente, Data_inizio, Data_fine, (julianday(Data_fine) - julianday(Data_inizio)) AS Durata_giorni FROM Terapia WHERE Data_fine IS NOT NULL; /* -------- 3.4 Operatori aggregati: DISTINCT, COUNT, SUM, AVG, MIN, MAX -------- */ -- DISTINCT: mostra solo valori diversi (non duplicati) SELECT DISTINCT Citta FROM Paziente; -- COUNT: numero di righe che soddisfano la condizione SELECT COUNT(*) AS Numero_pazienti FROM Paziente; -- COUNT di un attributo (ignora i NULL) SELECT COUNT(CodiceEsame) AS Pazienti_con_cartella FROM Paziente; -- SUM, AVG, MIN, MAX – esempio su una colonna numerica -- (qui facciamo un esempio artificiale con la durata delle terapie) SELECT SUM(julianday(Data_fine) - julianday(Data_inizio)) AS Somma_giorni, AVG(julianday(Data_fine) - julianday(Data_inizio)) AS Media_giorni, MIN(julianday(Data_fine) - julianday(Data_inizio)) AS Min_giorni, MAX(julianday(Data_fine) - julianday(Data_inizio)) AS Max_giorni FROM Terapia WHERE Data_fine IS NOT NULL; /* -------- 3.5 GROUP BY e HAVING -------- GROUP BY raggruppa le righe in base a uno o più attributi. HAVING permette di fare condizioni su operatori aggregati, un po' come WHERE ma dopo il raggruppamento. */ -- Quante terapie ha avuto ogni paziente? SELECT CF_paziente, COUNT(*) AS Numero_terapie FROM Terapia GROUP BY CF_paziente; -- Mostra SOLO i pazienti che hanno avuto più di 1 terapia SELECT CF_paziente, COUNT(*) AS Numero_terapie FROM Terapia GROUP BY CF_paziente HAVING COUNT(*) > 1; /* -------- 3.6 Operatore LIKE e pattern -------- % = qualunque numero (anche zero) di caratteri _ = esattamente UN carattere WHERE colonna LIKE 'pattern' Esempio dato: SELECT * FROM terapia WHERE PrincipioAttivo LIKE '%o'; (Nel nostro schema PrincipioAttivo è nella tabella Farmaci, quindi facciamo un esempio corretto con JOIN) */ -- Tutti i farmaci il cui principio attivo finisce in 'ina' SELECT * FROM Farmaci WHERE PrincipioAttivo LIKE '%ina'; -- Tutti i pazienti il cui cognome inizia con 'N' SELECT * FROM Paziente WHERE Cognome LIKE 'N%'; -- Cognomi che hanno esattamente 7 lettere e iniziano per 'N' SELECT * FROM Paziente WHERE Cognome LIKE 'N______'; -- N + 6 underscore = 7 caratteri totali /* ===================================================================== 4. ESEMPI DI QUERY PER LE DOMANDE 1.1–1.5 ===================================================================== */ /* 1.1 Conoscere cognome e nome dei pazienti maschi considerati nella base di dati; */ SELECT Cognome, Nome FROM Paziente WHERE Sesso = 'M'; /* 1.2 Conoscere tutti i dati relativi ai pazienti il cui cognome è “Nunziatella”; */ SELECT * FROM Paziente WHERE Cognome = 'Nunziatella'; /* 1.3 Conoscere cognome, nome, anno di nascita dei pazienti considerati, in ordine di cognome. Usiamo strftime('%Y', Data_Nascita) per estrarre l’anno come testo. */ SELECT Cognome, Nome, strftime('%Y', Data_Nascita) AS Anno_Nascita FROM Paziente ORDER BY Cognome ASC, Nome ASC; /* 1.4 Conoscere cognome, nome e età dei pazienti (usiamo julianday per calcolare un’età approssimata) */ SELECT Cognome, Nome, CAST((julianday('now') - julianday(Data_Nascita)) / 365.25 AS INTEGER) AS Eta FROM Paziente ORDER BY Cognome, Nome; /* 1.5 Conoscere principio attivo, ID paziente (CF) e data inizio delle terapie finite durante il 2019 "finite durante il 2019" -> Data_fine compresa tra 2019-01-01 e 2019-12-31. */ SELECT f.PrincipioAttivo, t.CF_paziente AS ID_paziente, t.Data_inizio FROM Terapia t JOIN Farmaci f ON t.CodiceFarmaco = f.CodiceFarmaco WHERE t.Data_fine BETWEEN '2019-01-01' AND '2019-12-31' ORDER BY t.Data_inizio; /* ===================================================================== 5. JOIN (INNER, LEFT, "RIGHT") – DEFINIZIONI E ESEMPI ===================================================================== */ /* INNER JOIN: restituisce solo le righe che hanno corrispondenza in entrambe le tabelle. LEFT JOIN: restituisce tutte le righe della tabella a sinistra, e solo le corrispondenti della tabella a destra (se non c’è corrispondenza, i campi della destra sono NULL). RIGHT JOIN: SQLite NON supporta RIGHT JOIN direttamente. Si può simulare con LEFT JOIN invertendo l'ordine delle tabelle. */ /* ---- 2.4 Conoscere tutte le terapie associate ad ogni paziente ---- Usiamo un LEFT JOIN per vedere anche pazienti che NON hanno terapie (se presenti). */ SELECT p.CF, p.Cognome, p.Nome, t.ID_terapia, t.Data_inizio, t.Data_fine FROM Paziente p LEFT JOIN Terapia t ON p.CF = t.CF_paziente ORDER BY p.Cognome, p.Nome, t.Data_inizio; /* ---- Esempio di "RIGHT JOIN" simulato ---- "Tutte le terapie con (eventuale) paziente" (di fatto è un INNER JOIN perché il vincolo di chiave esterna impone che il paziente esista). */ SELECT t.ID_terapia, t.Data_inizio, t.Data_fine, p.CF, p.Cognome, p.Nome FROM Terapia t LEFT JOIN Paziente p ON t.CF_paziente = p.CF; /* ---- 2.5 Conoscere cognome e nome dei pazienti trattati con warfarin ---- JOIN tra Paziente, Terapia, Farmaci */ SELECT DISTINCT p.Cognome, p.Nome FROM Paziente p JOIN Terapia t ON p.CF = t.CF_paziente JOIN Farmaci f ON t.CodiceFarmaco = f.CodiceFarmaco WHERE f.PrincipioAttivo = 'warfarin' ORDER BY p.Cognome, p.Nome; /* DISTINCT evita di ripetere lo stesso paziente se ha avuto più di una terapia con warfarin. */ /* ---- 2.6 Conoscere cognome, nome, data di nascita, terapia per quei pazienti la cui terapia è finita prima dell’anno 2015. ---- "prima dell’anno 2015" -> Data_fine < '2015-01-01' */ SELECT p.Cognome, p.Nome, p.Data_Nascita, f.PrincipioAttivo AS Terapia_PrincipioAttivo, t.Data_inizio, t.Data_fine FROM Paziente p JOIN Terapia t ON p.CF = t.CF_paziente JOIN Farmaci f ON t.CodiceFarmaco = f.CodiceFarmaco WHERE t.Data_fine < '2015-01-01' ORDER BY p.Cognome, p.Nome, t.Data_fine; /* ---- Esempio aggiuntivo: pazienti SENZA alcuna terapia ---- */ SELECT p.CF, p.Cognome, p.Nome FROM Paziente p LEFT JOIN Terapia t ON p.CF = t.CF_paziente WHERE t.ID_terapia IS NULL; /* ===================================================================== FINE DEL FILE DI ESERCIZI ===================================================================== */