WITH (CTE) în SQLite

Interogări avansate SQLite 3.8.3

Dă un nume unei subinterogări (Common Table Expression) și o folosește apoi ca pe un tabel, ca interogarea să fie mai ușor de citit.

Sintaxă
WITH nume [(coloane)] AS [[NOT] MATERIALIZED] (SELECT ...),
     alt_nume AS (SELECT ... FROM nume ...)
SELECT ... FROM alt_nume;

Ce face WITH (CTE)

O expresie de tabel comună (CTE) este un SELECT cu nume, scris în fața comenzii principale cu WITH. În comanda principală îl folosești ca pe un tabel obișnuit. Poți defini mai multe CTE-uri separate prin virgulă, iar fiecare le poate folosi pe cele de dinaintea lui.

În SQLite, WITH merge în fața lui SELECT, INSERT, UPDATE și DELETE. Un CTE poate avea propria listă de coloane și poate conține direct VALUES, ceea ce e util pentru câteva date de test fără să creezi un tabel.

Din 3.35.0 poți scrie AS MATERIALIZED (rezultatul se calculează o dată și se păstrează) sau AS NOT MATERIALIZED (SQLite îl poate integra în interogarea principală). Fără indicație, SQLite alege singur; un CTE folosit de mai multe ori este de obicei materializat.

Parametri

Parametru Descriere
nume Numele sub care folosești rezultatul în restul comenzii.
(coloane) Opțional: numele coloanelor rezultatului.
MATERIALIZED Opțional (3.35.0+): sugerează cum să fie calculat CTE-ul.

Exemple

Exemplele de pe această pagină pornesc de la datele de mai jos. Rulează mai întâi acest cod, apoi exemplul.

CREATE TABLE vanzari (
  id    INTEGER PRIMARY KEY,
  oras  TEXT NOT NULL,
  agent TEXT NOT NULL,
  suma  INTEGER NOT NULL
);
INSERT INTO vanzari (oras, agent, suma) VALUES
  ('Iași', 'Ana', 1200),
  ('Iași', 'Radu', 800),
  ('Cluj', 'Elena', 1500),
  ('Cluj', 'Dan', 400),
  ('Brașov', 'Mara', 950),
  ('Iași', 'Ana', 600);

Exemplul 1: Totaluri pe oraș, apoi filtrare

CTE-ul totale calculează suma pe oraș; interogarea principală păstrează doar orașele cu peste 1.000 de lei.

WITH totale AS (
  SELECT oras, SUM(suma) AS total
  FROM vanzari
  GROUP BY oras
)
SELECT oras, total
FROM totale
WHERE total > 1000
ORDER BY total DESC;

Rezultat

╭──────┬───────╮
│ oras │ total │
╞══════╪═══════╡
│ Iași │  2600 │
│ Cluj │  1900 │
╰──────┴───────╯

Exemplul 2: Două CTE-uri înlănțuite

pe_agent adună vânzările fiecărui agent, iar medie calculează media acestor totaluri. La final rămân agenții peste medie (media este 1.090 de lei).

WITH pe_agent AS (
  SELECT agent, SUM(suma) AS total FROM vanzari GROUP BY agent
),
medie AS (
  SELECT AVG(total) AS valoare FROM pe_agent
)
SELECT agent, total, (SELECT valoare FROM medie) AS media
FROM pe_agent
WHERE total > (SELECT valoare FROM medie)
ORDER BY total DESC;

Rezultat

╭───────┬───────┬────────╮
│ agent │ total │ media  │
╞═══════╪═══════╪════════╡
│ Ana   │  1800 │ 1090.0 │
│ Elena │  1500 │ 1090.0 │
╰───────┴───────┴────────╯

Exemplul 3: CTE cu VALUES și CTE înaintea lui UPDATE

Prima comandă folosește un CTE construit din VALUES, cu nume de coloane date în paranteză. A doua mărește cu 10% vânzările din orașele cu un singur agent; după UPDATE, vânzarea din Brașov a ajuns la 1.045 de lei.

WITH tinte (oras, obiectiv) AS (
  VALUES ('Iași', 2500), ('Cluj', 2000), ('Brașov', 1000)
)
SELECT t.oras, t.obiectiv, SUM(v.suma) AS realizat
FROM tinte t JOIN vanzari v ON v.oras = t.oras
GROUP BY t.oras
ORDER BY t.oras;

WITH orase_mici AS (
  SELECT oras FROM vanzari GROUP BY oras HAVING COUNT(DISTINCT agent) = 1
)
UPDATE vanzari SET suma = suma * 11 / 10
WHERE oras IN (SELECT oras FROM orase_mici);

SELECT oras, agent, suma FROM vanzari WHERE oras = 'Brașov';

Rezultat

╭────────┬──────────┬──────────╮
│  oras  │ obiectiv │ realizat │
╞════════╪══════════╪══════════╡
│ Brașov │     1000 │      950 │
│ Cluj   │     2000 │     1900 │
│ Iași   │     2500 │     2600 │
╰────────┴──────────┴──────────╯
╭────────┬───────┬──────╮
│  oras  │ agent │ suma │
╞════════╪═══════╪══════╡
│ Brașov │ Mara  │ 1045 │
╰────────┴───────┴──────╯

Greșeli frecvente

  • Pui virgulă după ultimul CTE, înainte de SELECT. Virgula separă doar CTE-urile între ele.
  • Folosești un CTE într-o altă comandă. Numele există doar în comanda în care l-ai definit; pentru reutilizare creează o vedere (CREATE VIEW).
  • Pui WITH în mijlocul unui INSERT ... SELECT și primești eroare. Scrie WITH la începutul comenzii: WITH x AS (...) INSERT INTO t SELECT ... FROM x;.

Comenzi înrudite

  • WITH RECURSIVE: Un CTE care se folosește pe el însuși: generează serii de numere sau de date și parcurge ierarhii (șef–subordonat, categorii).
  • ROW_NUMBER, RANK, LAG (funcții fereastră): Calculează pentru fiecare rând un număr de ordine, un loc în clasament sau valoarea din rândul vecin, fără să grupeze rândurile.
  • CREATE VIEW: Salvează un SELECT sub un nume, ca să îl folosești apoi ca pe un tabel. În SQLite, vederile sunt doar pentru citire.
  • EXPLAIN QUERY PLAN: Arată cum va executa SQLite o interogare: ce tabele citește în întregime (SCAN) și unde folosește un index (SEARCH).