rowid și INTEGER PRIMARY KEY în SQLite

Tabele și tipuri de date Toate versiunile SQLite 3

Orice tabel SQLite obișnuit are o coloană ascunsă rowid; o coloană INTEGER PRIMARY KEY devine alias al ei și primește automat valori.

Sintaxă
SELECT rowid, * FROM tabel;
CREATE TABLE t (id INTEGER PRIMARY KEY, ...);  -- id este alias pentru rowid
SELECT last_insert_rowid();

Ce face rowid și INTEGER PRIMARY KEY

Fiecare rând dintr-un tabel obișnuit are un număr întreg unic, rowid, chiar dacă nu l-ai declarat. Datele sunt păstrate pe disc în ordinea lui, așa că o căutare după rowid este cea mai rapidă căutare posibilă. Coloana se poate citi și cu numele oid sau _rowid_.

Dacă declari o coloană exact INTEGER PRIMARY KEY, ea devine un alt nume pentru rowid. Când inserezi fără valoare (sau cu NULL), SQLite îi dă automat valoarea cea mai mare din tabel plus 1. Funcția last_insert_rowid() îți spune ce valoare a primit ultimul rând inserat pe conexiunea curentă.

Atenție la scriere: doar INTEGER PRIMARY KEY devine alias. INT PRIMARY KEY sau BIGINT PRIMARY KEY creează o cheie primară obișnuită, separată de rowid, care, din motive istorice, acceptă chiar și NULL.

Ce returnează

last_insert_rowid() returnează rowid-ul ultimului rând inserat, ca întreg.

Exemple

Exemplul 1: rowid-ul ascuns

Tabelul nu are nicio coloană de identificare, dar fiecare rând are totuși un rowid. SELECT * nu îl afișează; trebuie cerut explicit.

CREATE TABLE note (elev TEXT, nota INTEGER);
INSERT INTO note VALUES ('Ana', 9), ('Mihai', 7);
SELECT rowid, * FROM note;

Rezultat

╭───────┬───────┬──────╮
│ rowid │ elev  │ nota │
╞═══════╪═══════╪══════╡
│     1 │ Ana   │    9 │
│     2 │ Mihai │    7 │
╰───────┴───────┴──────╯

Exemplul 2: INTEGER PRIMARY KEY primește valori automat

Primele două rânduri primesc 1 și 2. După ce inserăm explicit id-ul 10, următorul rând primește 11 (cel mai mare plus 1). După ștergerea lui 11, valoarea 11 este refolosită: fără AUTOINCREMENT, un id eliberat la final poate fi dat din nou.

CREATE TABLE elevi (id INTEGER PRIMARY KEY, nume TEXT);
INSERT INTO elevi (nume) VALUES ('Ana'), ('Mihai');
INSERT INTO elevi (id, nume) VALUES (10, 'Ioana');
INSERT INTO elevi (nume) VALUES ('Radu');
SELECT last_insert_rowid() AS ultimul_id;

DELETE FROM elevi WHERE id = 11;
INSERT INTO elevi (nume) VALUES ('Elena');
SELECT rowid AS r, id, nume FROM elevi;

Rezultat

╭────────────╮
│ ultimul_id │
╞════════════╡
│         11 │
╰────────────╯
╭────┬────┬───────╮
│ r  │ id │ nume  │
╞════╪════╪═══════╡
│  1 │  1 │ Ana   │
│  2 │  2 │ Mihai │
│ 10 │ 10 │ Ioana │
│ 11 │ 11 │ Elena │
╰────┴────┴───────╯

Exemplul 3: INT PRIMARY KEY nu este alias

Cu INT în loc de INTEGER, coloana id este o coloană separată: nu primește valoare automat și a rămas NULL, în timp ce rowid-ul ascuns este 1.

CREATE TABLE clienti (id INT PRIMARY KEY, nume TEXT);
INSERT INTO clienti (nume) VALUES ('Ana');
SELECT rowid AS r, id, typeof(id) AS tip_id, nume FROM clienti;

Rezultat

╭───┬────┬────────┬──────╮
│ r │ id │ tip_id │ nume │
╞═══╪════╪════════╪══════╡
│ 1 │    │ null   │ Ana  │
╰───┴────┴────────┴──────╯

Greșeli frecvente

  • Scrii id INT PRIMARY KEY și aștepți numere automate. Doar INTEGER PRIMARY KEY (exact acest tip) devine alias pentru rowid.
  • Te bazezi pe rowid într-un tabel fără INTEGER PRIMARY KEY ca identificator permanent. VACUUM poate renumerota rowid-urile acestor tabele; declară o coloană INTEGER PRIMARY KEY.
  • Citești last_insert_rowid() din altă conexiune sau după alte inserări. Valoarea este per conexiune și se schimbă la fiecare INSERT; în SQLite 3.35+ poți folosi INSERT ... RETURNING id.

Comenzi înrudite

  • AUTOINCREMENT: Garantează că un id nu este refolosit niciodată, nici după ștergere; SQLite ține ultima valoare în tabelul sqlite_sequence.
  • WITHOUT ROWID: Creează un tabel fără coloana ascunsă rowid, organizat direct după cheia primară; util pentru chei compuse sau texte scurte.
  • RETURNING: Face ca INSERT, UPDATE sau DELETE să returneze rândurile modificate, de exemplu id-ul generat, fără un SELECT separat.
  • CREATE TABLE: Creează un tabel nou. În SQLite tipul coloanei este opțional și doar sugerează cum se salvează valorile.
  • VACUUM: Reconstruiește fișierul bazei de date: eliberează spațiul rămas după ștergeri și, cu INTO, face o copie compactă.