Scegliere i data type giusti

La scelta del data type è la prima decisione di design e condiziona storage, uso di memoria, qualità dei piani di esecuzione e integrità. Il criterio guida è usare il tipo più piccolo che rappresenta correttamente il dominio del dato: INT invece di BIGINT quando i valori restano nel range, TINYINT/SMALLINT per enumerazioni ridotte, DECIMAL/NUMERIC per valori monetari esatti e FLOAT/REAL solo per grandezze approssimate. Per il testo, VARCHAR a lunghezza variabile evita sprechi rispetto a CHAR; NVARCHAR (Unicode) va usato solo quando serve il multilingua, perché raddoppia lo storage. Preferire DATETIME2 a DATETIME per precisione e range, e i tipi *(MAX) al posto dei deprecati TEXT/NTEXT/IMAGE.

Attenzione alle conversioni implicite: confrontare colonne di tipi diversi rende il predicato non-SARGable e impedisce l’uso degli indici. In ambito AI, SQL introduce il vector type per memorizzare gli embeddings e abilitare la similarity search; a livello di design va trattato come una colonna dedicata, senza assumerne la sintassi esatta ma sapendo che serve a query di vicinanza semantica.

Indici rowstore e columnstore

Gli indici rowstore sono strutture B-tree e si dividono in clustered e nonclustered. Il clustered index definisce l’ordine fisico delle righe: ne esiste uno solo per tabella ed è tipicamente la chiave di accesso principale. I nonclustered index sono strutture aggiuntive; con le included columns si costruiscono covering index che soddisfano una query senza key lookup. I rowstore eccellono nei lookup puntuali e nei workload OLTP.

Il columnstore index memorizza i dati per colonna, con forte compressione ed esecuzione in batch mode: è la scelta per workload analitici che aggregano molte righe. Un clustered columnstore index è indicato per grandi fact table di data warehouse; un nonclustered columnstore index abilita scenari HTAP, cioè analytics in tempo reale sulla stessa tabella OLTP senza spostare i dati. Il trade-off è chiaro: più indici accelerano le letture ma appesantiscono insert, update e delete oltre alla manutenzione.

Constraint, primary key e foreign key

I constraint spostano l’integrità dei dati dentro l’engine, dove è più affidabile che nel codice applicativo. La PRIMARY KEY garantisce unicità e non-nullità e, se non esiste già un clustered index, ne crea uno sulla colonna chiave. La scelta tra natural key e surrogate key (spesso IDENTITY o SEQUENCE) incide su performance e frammentazione. La FOREIGN KEY impone integrità referenziale tra tabelle e supporta azioni di cascade; è buona pratica indicizzare le colonne FK, perché join e delete sul lato padre altrimenti scandiscono la tabella figlia.

Completano il quadro UNIQUE (unicità alternativa alla PK, ammette un NULL), CHECK (regole di dominio), DEFAULT e NOT NULL. Ogni constraint aggiunge garanzie ma ha un costo in scrittura: il design bilancia le due esigenze in base al workload.

Trappole tipiche d’esame

  • Fact table da miliardi di righe per aggregazioni → clustered columnstore index: un clustered rowstore B-tree non regge le scansioni analitiche; il columnstore comprime e usa batch mode. Distinguere sempre dal caso OLTP con lookup puntuali, dove serve rowstore.
  • Analytics in tempo reale sulla tabella OLTP → nonclustered columnstore index (HTAP): non serve duplicare i dati in un data warehouse separato; il nonclustered columnstore affianca il rowstore esistente.
  • Join o delete lenti su colonna FK → creare un indice sulla FK: la foreign key non genera automaticamente un indice, a differenza della primary key. Errore tipico: credere che il constraint indicizzi da sé.
  • PRIMARY KEY su un GUID → attenzione al clustered default: definire la PK crea un clustered index; su un GUID casuale provoca frammentazione. Preferire un surrogate INT/BIGINT IDENTITY o clusterizzare su un’altra colonna.
  • Data type sovradimensionato o mismatch → tipo corretto e SARGability: NVARCHAR dove basta VARCHAR raddoppia lo storage; il confronto tra tipi diversi rende il predicato non-SARGable e ignora l’indice.
  • External table su SQL database in Fabric → non supportata: non assumere parità di feature con SQL Server o Synapse; per i dati esterni valutare altre opzioni e la documentazione ufficiale.