RQL for SQL Users
Everything you know about querying still applies; the syntax reads in a different direction. SQL describes the result you want from the inside out. RQL describes the steps to get there, top to bottom.
Translation table
| SQL | RQL |
|---|---|
| SELECT name, email FROM users | from app::users map { name, email } |
| WHERE role = 'admin' | filter { role == "admin" } |
| ORDER BY name ASC | sort { name: asc } |
| LIMIT 10 | take 10 |
| GROUP BY region | aggregate { total: math::sum(amount) } by { region } |
| SELECT DISTINCT category | distinct { category } |
| NULL | none |
| INSERT INTO t VALUES (1, ...) | INSERT app::t [{ id: 1, ... }] |
| UPDATE t SET x = 1 WHERE id = 2 | UPDATE app::t { x: 1 } FILTER { id == 2 } |
| DELETE FROM t WHERE id = 2 | DELETE app::t FILTER { id == 2 } |
There is no SELECT keyword in RQL. Projection is a pipeline step (map), not the frame of the whole query.
Reading order
A SQL query nests: SELECT ... FROM (SELECT ... FROM t WHERE ...) WHERE .... The equivalent RQL is flat; each line is one step, and intermediate results never need names:
GROUP BY becomes aggregate ... by
The aggregation expressions and the grouping keys live in one step. Nothing like HAVING is needed; a filter after the aggregate does the same job:
none is not NULL, mostly
RQL's missing value is none. Like SQL's NULL, it never matches an equality comparison:
Unlike SQL, the test for it is an ordinary function, not special syntax (IS NULL). And none is typed: a missing int4 still participates in type checking as an int4:
What has no SQL equivalent
- --Transactional views - materialized views maintained inside the writing transaction, not on a refresh schedule
- --Scripting - variables, control flow, and tests in the same language as queries
- --Arithmetic with explicit overflow policy -
math::add::saturate,math::add::none, and friends make numeric edge cases a choice instead of a surprise
