{
  "assessmentTests": {
    "sql_test": {
      "name": "Test SQL",
      "desc": "30 domande su scenari di filtraggio, join, aggregazione, subquery e window function — scopri se il tuo SQL corrisponde a ciò che un annuncio di lavoro intende per competenza SQL.",
      "recommendation": "Il tuo profilo di competenze SQL",
      "results": {
        "beginner": {
          "name": "Principiante",
          "desc": "Riesci a scrivere un'istruzione SELECT funzionante e filtrare su poche condizioni, ma le domande che hai sbagliato si concentrano su NULL handling e meccanica dei join piuttosto che sulla sintassi — confrontare una colonna con NULL usando =, cosa LEFT JOIN in realtà preserva, come funzionano i confini di BETWEEN. Niente di questo riguarda l'essere scarsi con i database; sono regole specifiche che trippano allo stesso modo chi ha imparato SQL per trial and error e chi ha studiato lo standard. Contano al lavoro perché ognuno di questi è il luogo dove una query ritorna un conteggio di righe plausibile ma sbagliato.",
          "recommendation": "Inizia con tre cose, in questo ordine: perché WHERE col = NULL non corrisponde mai a nulla (e IS NULL è richiesto), come INNER JOIN scarta le righe senza corrispondenza mentre LEFT JOIN le mantiene, e i confini esatti che BETWEEN include. Il tutorial SQL di Mode Analytics e la documentazione PostgreSQL coprono tutti e tre con esempi eseguibili."
        },
        "intermediate": {
          "name": "Intermedio",
          "desc": "Gestisci comodamente le query di reporting quotidiane — join, GROUP BY, filtraggio diretto — e non saresti rallentato dal lavoro di manutenzione di routine o dall'analisi ad hoc su una base di dati esistente. Il divario tra qui e Avanzato è per lo più in ciò che succede quando le clausole interagiscono: una clausola WHERE sulla tabella unita che silenziosamente trasforma un LEFT JOIN in un INNER JOIN, un join che moltiplica i conteggi delle righe prima che un'aggregazione venga applicata, un NOT IN che silenziosamente ritorna nulla perché un NULL è scivolato nella subquery. Questi sono il tipo di bug che passano un controllo veloce e appaiono solo quando qualcuno confronta il totale con un report diverso.",
          "recommendation": "Focalizzati su come le clausole interagiscono piuttosto che su cosa fa ciascuna singolarmente: filtraggio WHERE sulle colonne destra di un LEFT JOIN, moltiplicazione delle righe dai join uno-a-molti prima di aggregare, e perché NOT IN si spezza in presenza di NULL (EXISTS solitamente no). Poi HAVING vs WHERE, dato che questo split confonde chi conosce già entrambe le clausole singolarmente."
        },
        "advanced": {
          "name": "Avanzato",
          "desc": "Questo è il livello che la maggior parte degli annunci di lavoro intende con \"SQL forte.\" Leggi una query multi-join e puoi predire il suo conteggio di righe prima di eseguirla, sai perché un risultato è cambiato piuttosto che solo che è accaduto, e raggiungi una CTE o una window function invece di una subquery annidatA quando quello è lo strumento più chiaro. Ciò che separa questa banda dal top è il lato difensivo del lavoro: sapere quale ranking function usare quando i tie contano, cosa una transazione isola da una sessione concorrente, e cosa una regola ON DELETE mancante effettivamente fa a livello di database.",
          "recommendation": "Approfondisci le parti che proteggono i dati che altri toccano anche: la differenza tra RANK, DENSE_RANK e ROW_NUMBER sotto i tie, cosa COMMIT effettivamente rende visibile e a chi, e il comportamento della foreign key su DELETE. Usa la newsletter The Index o i deep dive di SQL for Devs su window function e transaction isolation come prossimi step per entrambi."
        },
        "expert": {
          "name": "Esperto",
          "desc": "Hai segnato il massimo in ogni sezione — filtraggio e NULL semantica, join e set operations, aggregazione e subquery, e window function, CTE e constraint. Praticamente, questo significa che puoi ricevere una query report multi-join di uno sconosciuto e spiegare perché ritorna il conteggio di righe che fa, non solo cosa la sintassi dice che dovrebbe fare, che è l'abilità più difficile e più preziosa. A questo livello il linguaggio query è raramente il fattore limitante; il limite è solitamente il design dello schema o la dimensione dei dati sotto di esso.",
          "recommendation": "I rendimenti ora sono negli execution plan e nel design: leggere l'output EXPLAIN prima di assumere che una query sia lenta, strategia di indicizzazione come un tradeoff contro il costo in scrittura piuttosto che una vittoria libera, e decisioni di normalizzazione che reggono mentre uno schema cresce. Se stai sendo selezionato per un ruolo, descrivi un bug di query come il join fan-out o la trappola NOT IN/NULL che hai trovato in produzione piuttosto che nominare feature SQL — dimostra il ragionamento, non solo il vocabolario."
        }
      },
      "questions": [
        {
          "question": "Una tabella ha una colonna nullable age. Esegui SELECT * FROM users WHERE age = NULL. Quante righe ritorna, anche se diverse righe hanno age impostato a NULL?",
          "options": [
            {
              "icon": "",
              "label": "Ogni riga dove age è NULL, perché = corrisponde NULL come qualsiasi altro valore"
            },
            {
              "icon": "",
              "label": "Zero — confrontare qualsiasi cosa con NULL usando = produce UNKNOWN, mai TRUE, quindi nessuna riga corrisponde; IS NULL è richiesto invece"
            },
            {
              "icon": "",
              "label": "Un syntax error — NULL non può apparire sul lato destro di ="
            },
            {
              "icon": "",
              "label": "Ogni riga della tabella, perché i confronti NULL di default sono TRUE"
            }
          ]
        },
        {
          "question": "Esegui SELECT DISTINCT department, role FROM employees. Cosa toglie i duplicati DISTINCT?",
          "options": [
            {
              "icon": "",
              "label": "La combinazione di department e role insieme — una riga è rimossa solo se entrambe le colonne corrispondono esattamente a un'altra riga"
            },
            {
              "icon": "",
              "label": "Solo i valori department duplicati, mantenendo ogni role"
            },
            {
              "icon": "",
              "label": "Solo i valori role duplicati, mantenendo ogni department"
            },
            {
              "icon": "",
              "label": "Nulla — DISTINCT funziona solo con una singola colonna"
            }
          ]
        },
        {
          "question": "Una colonna name è filtrata con WHERE name LIKE 'A_'. Quale di questi valori corrisponde: 'A', 'Al', 'Ana', 'Ally'?",
          "options": [
            {
              "icon": "",
              "label": "'A' — l'underscore è opzionale e corrisponde a zero o più caratteri"
            },
            {
              "icon": "",
              "label": "'Al' — l'underscore corrisponde esattamente a un carattere, quindi LIKE 'A_' corrisponde a qualsiasi stringa di due caratteri che inizia con A"
            },
            {
              "icon": "",
              "label": "'Ana' e 'Ally' — l'underscore corrisponde a qualsiasi numero di caratteri finali"
            },
            {
              "icon": "",
              "label": "Tutti e quattro i valori corrispondono, perché LIKE ignora la lunghezza"
            }
          ]
        },
        {
          "question": "Una colonna price è filtrata con WHERE price BETWEEN 10 AND 20. Le righe con price esattamente 10 o esattamente 20 sono incluse?",
          "options": [
            {
              "icon": "",
              "label": "No — BETWEEN esclude entrambi i confini"
            },
            {
              "icon": "",
              "label": "Sì — BETWEEN è inclusivo su entrambi i lati, equivalente a price >= 10 AND price <= 20"
            },
            {
              "icon": "",
              "label": "Solo price = 10 è incluso; il confine superiore è esclusivo"
            },
            {
              "icon": "",
              "label": "Solo price = 20 è incluso; il confine inferiore è esclusivo"
            }
          ]
        },
        {
          "question": "Una tabella orders ha 10 righe, e 3 di esse hanno shipped_date NULL. Cosa ritorna SELECT COUNT(*) FROM orders, e cosa ritorna SELECT COUNT(shipped_date) FROM orders?",
          "options": [
            {
              "icon": "",
              "label": "10, poi 10 — COUNT sempre conta le righe indipendentemente dai NULL"
            },
            {
              "icon": "",
              "label": "7, poi 7 — entrambe le forme saltano le righe con NULL"
            },
            {
              "icon": "",
              "label": "10, poi 7 — COUNT(*) conta ogni riga, COUNT(column) conta solo le righe dove quella colonna non è NULL"
            },
            {
              "icon": "",
              "label": "10, poi 3 — COUNT(column) conta solo i valori NULL"
            }
          ]
        },
        {
          "question": "Esegui SELECT name, salary FROM employees ORDER BY 2 DESC. A cosa si riferisce il 2?",
          "options": [
            {
              "icon": "",
              "label": "Un syntax error — ORDER BY accetta solo nomi di colonna, non numeri"
            },
            {
              "icon": "",
              "label": "La seconda riga del risultato"
            },
            {
              "icon": "",
              "label": "Un valore letterale di 2, usato come tiebreaker"
            },
            {
              "icon": "",
              "label": "La seconda colonna nella lista SELECT, salary — ORDER BY accetta il numero di posizione di una colonna come scorciatoia"
            }
          ]
        },
        {
          "question": "In standard SQL, a cosa si valuta 'Ana' || ' ' || 'Lee'?",
          "options": [
            {
              "icon": "",
              "label": "Un syntax error — SQL non ha operatore di concatenazione"
            },
            {
              "icon": "",
              "label": "3 — || è trattato come un booleano OR e ritorna un conteggio"
            },
            {
              "icon": "",
              "label": "'AnaLee' — || concatena senza preservare gli spazi"
            },
            {
              "icon": "",
              "label": "'Ana Lee' — || è l'operatore standard SQL di concatenazione di stringhe"
            }
          ]
        },
        {
          "question": "Quale di questi è esattamente equivalente a WHERE status IN ('open', 'pending', 'review')?",
          "options": [
            {
              "icon": "",
              "label": "WHERE status = 'open' AND status = 'pending' AND status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status != 'open' OR status != 'pending' OR status != 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status = 'open' OR status = 'pending' OR status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status LIKE 'open,pending,review'"
            }
          ]
        },
        {
          "question": "customers ha 100 righe; 20 di esse non hanno mai fatto un ordine. Esegui SELECT c.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id. I 20 clienti senza ordini appaiono nel risultato?",
          "options": [
            {
              "icon": "",
              "label": "Sì, una volta ciascuno, con o.id mostrato come NULL"
            },
            {
              "icon": "",
              "label": "Sì, ma solo se appaiono anche in una clausola WHERE"
            },
            {
              "icon": "",
              "label": "No — INNER JOIN ritorna solo le righe che hanno una corrispondenza in entrambe le tabelle, quindi i clienti senza ordini sono scartati completamente"
            },
            {
              "icon": "",
              "label": "Sì, duplicati una volta per colonna in orders"
            }
          ]
        },
        {
          "question": "Vuoi ogni cliente indipendentemente dal fatto che abbiano ordini, quindi scrivi SELECT c.id, o.total FROM customers c LEFT JOIN orders o ON c.id = o.customer_id WHERE o.total > 100. Questo ancora ritorna clienti senza ordini?",
          "options": [
            {
              "icon": "",
              "label": "No — filtrare su o.total nella clausola WHERE scarta le righe NULL che LEFT JOIN ha prodotto per i clienti senza corrispondenza, quindi si comporta come un INNER JOIN"
            },
            {
              "icon": "",
              "label": "Sì — LEFT JOIN sempre preserva ogni riga da customers indipendentemente da ciò che segue"
            },
            {
              "icon": "",
              "label": "Sì, con o.total mostrato come 0 per i clienti senza ordini"
            },
            {
              "icon": "",
              "label": "No — LEFT JOIN silenziosamente si converte in RIGHT JOIN quando una clausola WHERE è aggiunta"
            }
          ]
        },
        {
          "question": "Una tabella employees ha una colonna id e una colonna manager_id che punta al id di un'altra riga. Per elencare ogni dipendente accanto al nome del loro manager, unisci la tabella a se stessa: SELECT e.name, m.name FROM employees e JOIN employees m ON e.manager_id = m.id. Come si chiama questo pattern?",
          "options": [
            {
              "icon": "",
              "label": "Un cross join — ogni dipendente è abbinato con ogni manager"
            },
            {
              "icon": "",
              "label": "Un recursive join — recupera la catena di gestione completa"
            },
            {
              "icon": "",
              "label": "Un self-join — la stessa tabella è unita a se stessa usando due alias diversi"
            },
            {
              "icon": "",
              "label": "Questo è SQL non valido — una tabella non può essere unita a se stessa"
            }
          ]
        },
        {
          "question": "Due query SELECT con le stesse colonne sono combinate con UNION. Se entrambe le query ritornano una riga identica, quante copie di quella riga appaiono nel risultato finale?",
          "options": [
            {
              "icon": "",
              "label": "Una — UNION rimuove le righe duplicate nel risultato combinato; UNION ALL manterrebbe entrambe le copie"
            },
            {
              "icon": "",
              "label": "Due — UNION mantiene ogni riga da entrambe le query"
            },
            {
              "icon": "",
              "label": "Zero — UNION rimuove qualsiasi riga che appare in entrambe le query"
            },
            {
              "icon": "",
              "label": "Dipende da quale query ha elencato prima la riga"
            }
          ]
        },
        {
          "question": "sizes ha 3 righe e colors ha 4 righe. Quante righe ritorna SELECT * FROM sizes CROSS JOIN colors?",
          "options": [
            {
              "icon": "",
              "label": "7 — CROSS JOIN aggiunge i conteggi delle righe insieme"
            },
            {
              "icon": "",
              "label": "12 — un CROSS JOIN ritorna ogni combinazione possibile di righe da entrambe le tabelle (3 x 4)"
            },
            {
              "icon": "",
              "label": "3 — CROSS JOIN ritorna una riga per riga nella prima tabella"
            },
            {
              "icon": "",
              "label": "0 — CROSS JOIN richiede una condizione ON o ritorna nulla"
            }
          ]
        },
        {
          "question": "orders ha 1 riga per ordine #100, e order_items ha 3 righe per ordine #100 (una per item di riga). Esegui SELECT o.id, o.total FROM orders o JOIN order_items i ON o.id = i.order_id WHERE o.id = 100. Quante righe ritornano per l'ordine #100?",
          "options": [
            {
              "icon": "",
              "label": "3 — il join produce una riga di output per riga order_items corrispondente, quindi la singola riga dell'ordine #100 è ripetuta una volta per item di riga"
            },
            {
              "icon": "",
              "label": "1 — orders ha solo una riga per ordine #100, quindi il join non può produrne di più"
            },
            {
              "icon": "",
              "label": "4 — una riga per l'ordine più una per item di riga"
            },
            {
              "icon": "",
              "label": "0 — unire una tabella a una riga a una tabella a tre righe su una chiave non unica fallisce"
            }
          ]
        },
        {
          "question": "SELECT c.name, o.id FROM customers c RIGHT JOIN orders o ON c.id = o.customer_id ritorna le stesse righe di quale di questi?",
          "options": [
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o LEFT JOIN customers c ON c.id = o.customer_id — scambiare l'ordine delle tabelle e usare LEFT JOIN invece di RIGHT JOIN è equivalente"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c LEFT JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o CROSS JOIN customers c"
            }
          ]
        },
        {
          "question": "In standard SQL, esegui SELECT department, name, AVG(salary) FROM employees GROUP BY department. È una query valida?",
          "options": [
            {
              "icon": "",
              "label": "Sì — GROUP BY deve includere solo department perché è elencato per primo"
            },
            {
              "icon": "",
              "label": "No — name è selezionato ma non è né aggregato né elencato in GROUP BY, e lo standard SQL richiede che ogni colonna non aggregata selezionata appaia nella clausola GROUP BY"
            },
            {
              "icon": "",
              "label": "Sì — SQL sceglie automaticamente un nome arbitrario per department"
            },
            {
              "icon": "",
              "label": "No — AVG() non può essere combinato con GROUP BY nella stessa query"
            }
          ]
        },
        {
          "question": "Vuoi reparti il cui salary medio supera 80000. Quale clausola filtra su un valore aggregato come AVG(salary) dopo il raggruppamento — WHERE o HAVING?",
          "options": [
            {
              "icon": "",
              "label": "WHERE — HAVING è usato solo con query UNION"
            },
            {
              "icon": "",
              "label": "Entrambi funzionano identicamente con funzioni aggregate"
            },
            {
              "icon": "",
              "label": "Nessuno — il filtraggio aggregato richiede una subquery"
            },
            {
              "icon": "",
              "label": "HAVING — WHERE filtra singole righe prima che il raggruppamento accada, HAVING filtra i gruppi dopo l'aggregazione"
            }
          ]
        },
        {
          "question": "Scrivi SELECT salary * 1.1 AS new_salary FROM employees WHERE new_salary > 50000. Questo si esegue?",
          "options": [
            {
              "icon": "",
              "label": "Sì — gli alias definiti in SELECT sono sempre disponibili a WHERE nella stessa query"
            },
            {
              "icon": "",
              "label": "Sì, ma solo per alias numerici"
            },
            {
              "icon": "",
              "label": "No — AS non è permesso dentro una query filtrata WHERE"
            },
            {
              "icon": "",
              "label": "No — WHERE è valutato prima che SELECT assegni l'alias new_salary, quindi l'alias non esiste ancora a quel punto nell'esecuzione"
            }
          ]
        },
        {
          "question": "SELECT name FROM employees e WHERE salary > (SELECT AVG(salary) FROM employees WHERE department = e.department). Perché questo si chiama una correlated subquery?",
          "options": [
            {
              "icon": "",
              "label": "Perché usa un JOIN invece di una clausola WHERE"
            },
            {
              "icon": "",
              "label": "La subquery interna riferisce e.department dalla query esterna, quindi deve essere ri-valutata per ogni riga che la query esterna considera"
            },
            {
              "icon": "",
              "label": "Perché ritorna più di una colonna"
            },
            {
              "icon": "",
              "label": "Perché viene eseguita esattamente una volta prima che la query esterna inizi"
            }
          ]
        },
        {
          "question": "Una subquery SELECT manager_id FROM employees ritorna alcuni valori NULL insieme agli id reali. Esegui SELECT name FROM employees WHERE id NOT IN (SELECT manager_id FROM employees). Cosa accade?",
          "options": [
            {
              "icon": "",
              "label": "Ritorna ogni dipendente che non è un manager, ignorando i NULL"
            },
            {
              "icon": "",
              "label": "Ritorna zero righe — un singolo NULL nella lista NOT IN rende ogni confronto UNKNOWN, quindi nessuna riga può soddisfare la condizione"
            },
            {
              "icon": "",
              "label": "Solleva un errore perché NOT IN non può essere usato con subquery"
            },
            {
              "icon": "",
              "label": "Ritorna ogni dipendente, poiché NULL è trattato come un wildcard"
            }
          ]
        },
        {
          "question": "SELECT name FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id). Cosa ritorna questo?",
          "options": [
            {
              "icon": "",
              "label": "Ogni cliente che ha almeno una riga in orders — EXISTS controlla solo se la subquery ritorna qualsiasi riga, non quali valori contengono"
            },
            {
              "icon": "",
              "label": "Ogni cliente, perché SELECT 1 sempre ritorna true"
            },
            {
              "icon": "",
              "label": "Un errore, perché la subquery seleziona un numero invece di un nome di colonna"
            },
            {
              "icon": "",
              "label": "Solo clienti con esattamente un ordine"
            }
          ]
        },
        {
          "question": "Scrivi SELECT name, (SELECT order_id FROM orders WHERE customer_id = c.id) AS last_order FROM customers c, e un dato cliente ha 3 righe in orders. Cosa accade quando questa query si esegue?",
          "options": [
            {
              "icon": "",
              "label": "Ritorna il primo order_id corrispondente e silenziosamente ignora gli altri due"
            },
            {
              "icon": "",
              "label": "Ritorna un elenco separato da virgole di tutti e tre gli order_id"
            },
            {
              "icon": "",
              "label": "Ritorna 3 righe per quel cliente, una per ordine"
            },
            {
              "icon": "",
              "label": "Solleva un errore al runtime — una subquery scalare nella lista SELECT deve ritornare al massimo una riga, e questa ne ritorna tre"
            }
          ]
        },
        {
          "question": "Quattro righe si legano per il punteggio più alto. Usando RANK() ORDER BY score DESC, tutti e quattro ottengono rank 1. Quale rank ottiene la riga successiva più bassa?",
          "options": [
            {
              "icon": "",
              "label": "5 — RANK() lascia un gap uguale al numero di righe legate prima di continuare"
            },
            {
              "icon": "",
              "label": "2 — RANK() sempre incrementa di esattamente uno dopo qualsiasi tie"
            },
            {
              "icon": "",
              "label": "1 — ogni riga successiva ottiene anche rank 1"
            },
            {
              "icon": "",
              "label": "4 — RANK() ricomincia il conteggio dal numero di tie"
            }
          ]
        },
        {
          "question": "Quattro righe si legano per il punteggio più alto. Usando DENSE_RANK() ORDER BY score DESC, tutti e quattro ottengono rank 1. Quale rank ottiene la riga successiva più bassa?",
          "options": [
            {
              "icon": "",
              "label": "5 — DENSE_RANK() si comporta esattamente come RANK()"
            },
            {
              "icon": "",
              "label": "1 — DENSE_RANK() assegna lo stesso rank a ogni riga rimanente"
            },
            {
              "icon": "",
              "label": "2 — DENSE_RANK() non lascia mai gap, quindi il prossimo valore distinto ottiene sempre il prossimo rank consecutivo"
            },
            {
              "icon": "",
              "label": "3 — DENSE_RANK() salta un rank per ogni gruppo di tie"
            }
          ]
        },
        {
          "question": "SUM(amount) OVER (PARTITION BY region ORDER BY amount) è aggiunto a una query. Cosa fa PARTITION BY region qui?",
          "options": [
            {
              "icon": "",
              "label": "Ricomincia la somma in corso separatamente per ogni regione, invece di accumulare nel set di risultati intero"
            },
            {
              "icon": "",
              "label": "Filtra i risultati a una singola regione"
            },
            {
              "icon": "",
              "label": "Raggruppa e collassa le righe in una per regione, come GROUP BY"
            },
            {
              "icon": "",
              "label": "Ordina le regioni alfabeticamente prima di sommare"
            }
          ]
        },
        {
          "question": "ROW_NUMBER() OVER (ORDER BY score DESC) è applicato a cinque righe, due delle quali sono esattamente legate nel punteggio. Possono due righe mai ricevere lo stesso row number?",
          "options": [
            {
              "icon": "",
              "label": "Sì — le righe legate sempre condividono lo stesso row number"
            },
            {
              "icon": "",
              "label": "Solo se PARTITION BY è anche usato"
            },
            {
              "icon": "",
              "label": "Dipende da se il tie è nella prima o ultima posizione"
            },
            {
              "icon": "",
              "label": "No — ROW_NUMBER() sempre assegna un intero unico, strettamente crescente a ogni riga, anche quando i valori sono legati"
            }
          ]
        },
        {
          "question": "WITH high_earners AS (SELECT * FROM employees WHERE salary > 100000) SELECT department, COUNT(*) FROM high_earners GROUP BY department. Cosa è high_earners?",
          "options": [
            {
              "icon": "",
              "label": "Una common table expression (CTE) — un set di risultati nominato, temporaneo che il resto della query può referenziare come una tabella"
            },
            {
              "icon": "",
              "label": "Una tabella permanente creata nel database"
            },
            {
              "icon": "",
              "label": "Una view che persiste dopo che la query finisce"
            },
            {
              "icon": "",
              "label": "Una stored procedure che deve essere chiamata separatamente"
            }
          ]
        },
        {
          "question": "Una colonna è dichiarata PRIMARY KEY. Puoi inserire una riga dove quella colonna è NULL?",
          "options": [
            {
              "icon": "",
              "label": "Sì — PRIMARY KEY impone solo l'unicità, non la non-nullità"
            },
            {
              "icon": "",
              "label": "Sì, ma solo una riga NULL è permessa, come un constraint UNIQUE"
            },
            {
              "icon": "",
              "label": "No — una colonna PRIMARY KEY è implicitamente NOT NULL, quindi inserire NULL è rigettato"
            },
            {
              "icon": "",
              "label": "Dipende da se la colonna ha anche un valore predefinito"
            }
          ]
        },
        {
          "question": "Dentro una transazione aperta, esegui un UPDATE ma non hai ancora eseguito COMMIT. Da una seconda, separata connessione al database, quell'update è visibile?",
          "options": [
            {
              "icon": "",
              "label": "Sì — tutte le connessioni vedono ogni scrittura nell'istante in cui esegue"
            },
            {
              "icon": "",
              "label": "Sì, ma solo se la seconda connessione apre anche una transazione"
            },
            {
              "icon": "",
              "label": "Dipende solo da quale tabella è stata aggiornata"
            },
            {
              "icon": "",
              "label": "No — una modifica non committata è visibile solo dentro la transazione che l'ha fatta, fino a che COMMIT la rende durabile e visibile agli altri"
            }
          ]
        },
        {
          "question": "products.category_id ha un constraint FOREIGN KEY referenziando categories.id. Provi a DELETE una riga da categories che ancora ha products che vi puntano, con nessuna regola ON DELETE specificata. Cosa accade?",
          "options": [
            {
              "icon": "",
              "label": "La riga category è cancellata e i corrispondenti valori products.category_id sono automaticamente impostati a NULL"
            },
            {
              "icon": "",
              "label": "La riga category è cancellata e ogni product che la referenziava è cancellato anche"
            },
            {
              "icon": "",
              "label": "Il DELETE è rigettato — il comportamento di foreign key predefinito blocca il cancellamento di una riga referenziata mentre righe dipendenti ancora vi puntano"
            },
            {
              "icon": "",
              "label": "Il DELETE succeede silenziosamente, lasciando il products' category_id puntare a una categoria che non esiste più"
            }
          ]
        }
      ],
      "optionOrderVersion": "e0466e547cfd8a45"
    }
  }
}
