DML & Queries
SQL Reference: DML
Section titled “SQL Reference: DML”Data Manipulation Language statements for inserting, updating, and deleting data.
INSERT
Section titled “INSERT”-- With RETURNINGINSERT INTO users (name, email) VALUES ('Dave', 'dave@example.com')ON CONFLICT (email) DO NOTHING RETURNING id, name, email;-- Single rowINSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
-- Multi-rowINSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com'), ('Bob', 'bob@example.com');
-- INSERT ... SELECTINSERT INTO archive SELECT * FROM users WHERE active = false;
-- With RETURNINGINSERT INTO users (name) VALUES ('Alice') RETURNING id, name;INSERT ON CONFLICT (Upsert)
Section titled “INSERT ON CONFLICT (Upsert)”-- Skip conflicting rowsINSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice')ON CONFLICT (email) DO NOTHING;
-- Upsert by columnINSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice Updated')ON CONFLICT (email) DO UPDATE SET name = excluded.name;
-- Upsert by constraint nameINSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice')ON CONFLICT ON CONSTRAINT users_email_key DO UPDATE SET name = excluded.name;The excluded pseudo-table is available in DO UPDATE SET clauses, referencing the row that was proposed for insertion.
UPDATE
Section titled “UPDATE”UPDATE users SET active = true WHERE name = 'Charlie' RETURNING *;UPDATE users SET name = 'Bob' WHERE id = 1;UPDATE users SET name = 'Bob' WHERE id = 1 RETURNING *;DELETE
Section titled “DELETE”DELETE FROM users WHERE id = 1;DELETE FROM users WHERE id = 1 RETURNING *;Foreign Key Referential Actions
Section titled “Foreign Key Referential Actions”Supported actions for ON DELETE and ON UPDATE: CASCADE, SET NULL, SET DEFAULT, RESTRICT, NO ACTION. A referencing table without a primary key cannot declare ON DELETE SET NULL / SET DEFAULT or ON UPDATE CASCADE / SET NULL / SET DEFAULT (0A000); give it a primary key first. Recursive cascade operations include cycle detection. MATCH SIMPLE semantics (PostgreSQL default): if any referencing column is NULL, the FK check is skipped.
SQL Reference: Queries
Section titled “SQL Reference: Queries”SELECT query features, JOINs, window functions, CTEs, subqueries, and set operations.
| Join Type | Supported |
|---|---|
INNER JOIN | Yes |
LEFT [OUTER] JOIN | Yes |
RIGHT [OUTER] JOIN | Yes |
FULL [OUTER] JOIN | Yes |
CROSS JOIN | Yes |
LATERAL JOIN | Yes — including correlated bodies, a WITH (or WITH RECURSIVE) clause inside the body, and correlated scalar/EXISTS/IN/ANY/ARRAY subqueries inside the body; see the compatibility matrix |
Semi join (via EXISTS) | Yes |
Anti join (via NOT EXISTS) | Yes |
Both hash join and nested-loop join implementations are available. The optimizer uses cost-based join reordering (DPccp algorithm).
Aggregation
Section titled “Aggregation”GROUP BYwith hash-based aggregationHAVINGclauseDISTINCTaggregates:COUNT(DISTINCT col)FILTERclause:COUNT(*) FILTER (WHERE x > 0)GROUPING()function
Window Functions
Section titled “Window Functions”SELECT name, department, salary, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rankFROM employees;Supported ranking functions: ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST
Supported access functions: LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE
Only SUM, AVG, COUNT, MIN, MAX and STRING_AGG may also be used as window functions —
every other aggregate fails with an OVER clause, with an error that names the function. See
Window Functions for the measured list.
Common Table Expressions (CTEs)
Section titled “Common Table Expressions (CTEs)”-- Standard CTEWITH active_users AS ( SELECT * FROM users WHERE active = true)SELECT * FROM active_users;
-- Recursive CTEWITH RECURSIVE tree AS ( SELECT id, parent_id, name, 1 AS depth FROM categories WHERE parent_id IS NULL UNION ALL SELECT c.id, c.parent_id, c.name, t.depth + 1 FROM categories c JOIN tree t ON c.parent_id = t.id)SELECT * FROM tree;Recursive CTEs run for at most 1,000 iterations; beyond that the query fails with
54001 — statement exceeds server complexity limit.
Every definition in one WITH list needs a distinct name: a duplicate fails with 42712
(WITH query name "a" specified more than once), as in PostgreSQL. A name can still be reused
across separate scopes — two sibling subqueries that each define a return their own values, and
a nested WITH a shadows an outer one without disturbing it.
A WITH list may contain INSERT/UPDATE/DELETE … RETURNING definitions in any position,
including after a helper definition they read from. This works the same over the
HTTP SQL API (which is what db9 db sql uses) and the PostgreSQL wire protocol:
WITH src AS (SELECT id FROM staging WHERE ready), ins AS (INSERT INTO target SELECT id FROM src RETURNING id)SELECT count(*) FROM ins;A WITH definition that is never referenced is not executed, as in PostgreSQL — a CTE that calls
a volatile function such as nextval() has no effect when nothing selects from it.
Subqueries
Section titled “Subqueries”- Scalar subqueries
EXISTS/NOT EXISTSIN/NOT INwith subqueryANY/ALLwith subquery- Array subqueries
- Correlated subqueries (with subquery decorrelation optimization)
All of these also work inside a LATERAL body, correlated or not, in any position — select list,
VALUES element, WHERE, GROUP BY, HAVING, ORDER BY or a function argument.
Set Operations
Section titled “Set Operations”| Operation | Supported |
|---|---|
UNION / UNION ALL | Yes |
INTERSECT / INTERSECT ALL | Yes |
EXCEPT / EXCEPT ALL | Yes |
Other Query Features
Section titled “Other Query Features”SELECT DISTINCTandSELECT DISTINCT ON (expr)ORDER BYwithASC/DESCandNULLS FIRST/NULLS LASTLIMITandOFFSETEXPLAINandEXPLAIN ANALYZEfor query plansPREPARE name AS .../EXECUTE name (params)/DEALLOCATE namegenerate_series()table functionSHOW TABLES