Passa al contenuto principale

SQLite

SQLite memorizza ogni tabella come un B-tree. Capire su quale chiave quel B-tree è ordinato è la chiave per applicare le best practice sugli indici.

Il rowid è il clustered index​

Per default ogni tabella ha una colonna implicita rowid: un intero a 64 bit che è la chiave di ordinamento fisico del B-tree. È il clustered index di SQLite, anche se non si chiama così.

Quando si dichiara una colonna come INTEGER PRIMARY KEY, questa diventa un alias del rowid: non si crea una colonna in più, la PK è il rowid e quindi la chiave fisica.

CREATE TABLE movimenti (
id INTEGER PRIMARY KEY, -- alias di rowid: ordina fisicamente per inserimento
conto INTEGER NOT NULL,
data TEXT NOT NULL,
importo INTEGER NOT NULL
);

Questa è esattamente la situazione che le best practice sconsigliano di subire passivamente: la tabella è ordinata per ordine di inserimento, non per il criterio con cui la si legge.

:::warning Attenzione all'alias INTEGER PRIMARY KEY diventa alias del rowid solo con il tipo esatto INTEGER. Scrivere INT PRIMARY KEY (o qualsiasi altra affinità) crea invece una normale PK separata e lascia il rowid nascosto: due chiavi anziché una. Se si vuole l'alias, il tipo dev'essere INTEGER. :::

Clustering semantico: tabelle WITHOUT ROWID​

Per ordinare fisicamente la tabella su un criterio scelto (il caso d'uso di lettura più comune) si usa una tabella WITHOUT ROWID. In questo caso non esiste più il rowid: la chiave primaria diventa direttamente la chiave di clustering del B-tree.

CREATE TABLE movimenti (
conto INTEGER NOT NULL,
data TEXT NOT NULL,
seq INTEGER NOT NULL,
importo INTEGER NOT NULL,
PRIMARY KEY (conto, data, seq) -- ordinamento fisico per conto e data
) WITHOUT ROWID;

Qui le righe di uno stesso conto, ordinate per data, stanno fisicamente vicine: una query «movimenti del conto X tra due date» è una lettura sequenziale. La colonna seq rende la chiave univoca senza alterare il criterio di accesso.

WITHOUT ROWID conviene quando la PK è naturale e non un intero crescente, e quando si accede spesso per intervallo su quella chiave. Va valutata caso per caso: per righe larghe può aumentare la dimensione degli indici secondari, che duplicano la PK completa.

Unicità separata dall'ordinamento​

L'unicità di una chiave logica diversa da quella fisica si esprime con un indice dedicato, coerentemente con la regola generale di non caricare l'unicità sulla chiave fisica:

-- la tabella è ordinata per (conto, data, seq);
-- il codice di riferimento esterno dev'essere comunque univoco
CREATE UNIQUE INDEX ux_movimenti_riferimento ON movimenti (riferimento);

In breve​

  • Il rowid (o l'INTEGER PRIMARY KEY che ne è alias) è il clustered index di default: ordina per inserimento.
  • Per ordinare fisicamente su un criterio semantico si usa WITHOUT ROWID con una PK composita scelta sul caso d'uso di lettura.
  • L'unicità di altre chiavi logiche va in indici UNIQUE dedicati, non nella chiave di clustering.