docs(website): add SQL queries reference page (advanced query surface)
New dedicated Reference page for the advanced-mode SQL query features under-covered by "Querying & inspecting": DISTINCT, GROUP BY/HAVING, set operations (UNION/INTERSECT/EXCEPT), subqueries (IN + correlated NOT EXISTS), CTEs (WITH), and expressions (CASE/CAST/functions) — each with a worked example on the library schema and real captured output. Adds an explicit "supported subset" boundary note (views, triggers, transactions, window functions, multi-statement batches are not available) rather than linking to general SQL, which would advertise unsupported features. Grounded in ADR-0030 §3/§13 and the SQL grammar tests. Cross-link added from Querying & inspecting. Build clean (26 pages); box-drawing output verified; forbidden terms clean.
This commit is contained in:
@@ -92,8 +92,10 @@ in detail — `show relationship` draws the two-table diagram covered in
|
||||
## Querying in advanced mode
|
||||
|
||||
Advanced mode runs full SQL `select`, including projections, `order by`,
|
||||
joins, `group by`, set operations, and `with` (CTEs). A projection of two
|
||||
columns, newest first:
|
||||
joins, `group by`, set operations, and `with` (CTEs). The richer query
|
||||
features — `distinct`, `having`, set operations, subqueries, CTEs, and `case` /
|
||||
`cast` — have their own page: [SQL queries](/reference/sql-queries/). A
|
||||
projection of two columns, newest first:
|
||||
|
||||
```sql
|
||||
select title, published from books where published > 1968 order by published desc
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
---
|
||||
title: SQL queries
|
||||
description: The advanced-mode SQL query surface — DISTINCT, GROUP BY/HAVING, set operations, subqueries, CTEs, and expressions.
|
||||
sidebar:
|
||||
order: 9
|
||||
---
|
||||
|
||||
[Querying & inspecting](/reference/querying-and-inspecting/) covers viewing rows
|
||||
and the basics of `select`, and the [Querying with joins](/guides/querying-with-joins/)
|
||||
guide covers joins, `group by`, and `order by`. This page documents the rest of
|
||||
the **advanced-mode** query surface: the features you reach for once a single
|
||||
table is not enough.
|
||||
|
||||
Everything here is **advanced mode**, so switch first with `mode advanced` (or
|
||||
prefix a single statement with `:`). The examples use the
|
||||
[example library](/getting-started/example-library/).
|
||||
|
||||
## DISTINCT
|
||||
|
||||
`distinct` removes duplicate rows from the result. Le Guin wrote two books, but
|
||||
her `author_id` appears once:
|
||||
|
||||
```sql
|
||||
select distinct author_id from books order by author_id
|
||||
```
|
||||
|
||||
```
|
||||
┌───────────┐
|
||||
│ author_id │
|
||||
├───────────┤
|
||||
│ 1 │
|
||||
│ 2 │
|
||||
│ 3 │
|
||||
└───────────┘
|
||||
```
|
||||
|
||||
`distinct` also works inside an aggregate — `count(distinct author_id)` counts
|
||||
the distinct authors.
|
||||
|
||||
## GROUP BY and HAVING
|
||||
|
||||
`group by` collapses rows that share a value into one row per group, so an
|
||||
aggregate (`count`, `sum`, `avg`, …) summarises each group. `having` then
|
||||
filters the *groups* — like `where`, but applied after grouping. Which authors
|
||||
have written more than one book?
|
||||
|
||||
```sql
|
||||
select authors.name, count(*) as books from books
|
||||
join authors on books.author_id = authors.author_id
|
||||
group by authors.name
|
||||
having count(*) > 1
|
||||
```
|
||||
|
||||
```
|
||||
┌───────────────────┬───────┐
|
||||
│ name │ books │
|
||||
├───────────────────┼───────┤
|
||||
│ Ursula K. Le Guin │ 2 │
|
||||
└───────────────────┴───────┘
|
||||
```
|
||||
|
||||
Use `where` to filter rows *before* grouping and `having` to filter groups
|
||||
*after* — that is the distinction between the two clauses.
|
||||
|
||||
## Set operations
|
||||
|
||||
`union`, `intersect`, and `except` combine the results of two queries that have
|
||||
matching columns. `union` merges and removes duplicates (`union all` keeps
|
||||
them); `intersect` keeps rows in both; `except` keeps rows in the first but not
|
||||
the second. Everyone associated with the library — authors and members — in one
|
||||
list:
|
||||
|
||||
```sql
|
||||
select name from authors
|
||||
union
|
||||
select name from members
|
||||
order by name
|
||||
```
|
||||
|
||||
```
|
||||
┌───────────────────┐
|
||||
│ name │
|
||||
├───────────────────┤
|
||||
│ Alan Turing │
|
||||
│ Grace Hopper │
|
||||
│ Italo Calvino │
|
||||
│ Jorge Luis Borges │
|
||||
│ Octavia E. Butler │
|
||||
│ Ursula K. Le Guin │
|
||||
└───────────────────┘
|
||||
```
|
||||
|
||||
## Subqueries
|
||||
|
||||
A subquery is a `select` nested inside another statement. Use one with `in` to
|
||||
test membership — the books written by authors born before 1925:
|
||||
|
||||
```sql
|
||||
select title from books
|
||||
where author_id in (select author_id from authors where birth_year < 1925)
|
||||
```
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ title │
|
||||
├──────────────────┤
|
||||
│ Invisible Cities │
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
A **correlated** subquery refers back to the outer query, so it is evaluated per
|
||||
outer row. With `not exists`, that finds authors who have *no* books:
|
||||
|
||||
```sql
|
||||
select name from authors a
|
||||
where not exists (select 1 from books b where b.author_id = a.author_id)
|
||||
```
|
||||
|
||||
```
|
||||
┌───────────────────┐
|
||||
│ name │
|
||||
├───────────────────┤
|
||||
│ Jorge Luis Borges │
|
||||
└───────────────────┘
|
||||
```
|
||||
|
||||
Scalar subqueries (one returning a single value) may also appear in a `select`
|
||||
projection or a `where` comparison.
|
||||
|
||||
## Common table expressions (WITH)
|
||||
|
||||
A common table expression names a query so the main statement can read from it
|
||||
like a table — useful for breaking a complex query into readable steps. Count
|
||||
each author's books in a CTE, then join to it for the names:
|
||||
|
||||
```sql
|
||||
with book_counts as (
|
||||
select author_id, count(*) as n from books group by author_id
|
||||
)
|
||||
select authors.name, book_counts.n as books from authors
|
||||
join book_counts on authors.author_id = book_counts.author_id
|
||||
order by book_counts.n desc, authors.name
|
||||
```
|
||||
|
||||
```
|
||||
┌───────────────────┬───────┐
|
||||
│ name │ books │
|
||||
├───────────────────┼───────┤
|
||||
│ Ursula K. Le Guin │ 2 │
|
||||
│ Italo Calvino │ 1 │
|
||||
│ Octavia E. Butler │ 1 │
|
||||
└───────────────────┴───────┘
|
||||
```
|
||||
|
||||
CTEs can be chained (`with a as (…), b as (…)`) and may be **recursive**
|
||||
(`with recursive …`) for hierarchical data.
|
||||
|
||||
## Expressions in queries
|
||||
|
||||
Anywhere a value is expected — a `select` projection, `where`, `having` — you
|
||||
can write an expression: arithmetic, comparisons, `like` / `in` / `between` /
|
||||
`is null`, **`case`**, **`cast`**, and function calls. (This is the same
|
||||
expression grammar used by simple-mode `where` and by `check` constraints.)
|
||||
|
||||
`case` chooses a value per row:
|
||||
|
||||
```sql
|
||||
select title, case when published < 1970 then 'classic' else 'modern' end as era
|
||||
from books order by book_id
|
||||
```
|
||||
|
||||
```
|
||||
┌───────────────────────────┬─────────┐
|
||||
│ title │ era │
|
||||
├───────────────────────────┼─────────┤
|
||||
│ A Wizard of Earthsea │ classic │
|
||||
│ The Left Hand of Darkness │ classic │
|
||||
│ Invisible Cities │ modern │
|
||||
│ Kindred │ modern │
|
||||
└───────────────────────────┴─────────┘
|
||||
```
|
||||
|
||||
Functions and `cast` work as you would expect:
|
||||
|
||||
```sql
|
||||
select name, length(name) as letters from authors order by author_id
|
||||
```
|
||||
|
||||
```
|
||||
┌───────────────────┬─────────┐
|
||||
│ name │ letters │
|
||||
├───────────────────┼─────────┤
|
||||
│ Ursula K. Le Guin │ 17 │
|
||||
│ Italo Calvino │ 13 │
|
||||
│ Octavia E. Butler │ 17 │
|
||||
└───────────────────┴─────────┘
|
||||
```
|
||||
|
||||
## The supported subset
|
||||
|
||||
Advanced mode covers a **teaching-focused subset of standard SQL** — enough to
|
||||
learn real query writing without the full surface of a production database. The
|
||||
query features above, plus joins, `order by`, and `limit`/`offset`, are all
|
||||
available, in `select` and in `insert` / `update` / `delete`.
|
||||
|
||||
Some things are deliberately **not** available, and will report an error if you
|
||||
try them:
|
||||
|
||||
- views and triggers,
|
||||
- transactions (`begin` / `commit` / `rollback`),
|
||||
- window functions (`… over (…)`),
|
||||
- multiple statements in one command (one statement per line).
|
||||
|
||||
To see how any query runs, prefix it with `explain` (see
|
||||
[Querying & inspecting](/reference/querying-and-inspecting/#query-plans)).
|
||||
|
||||
See also [Querying & inspecting](/reference/querying-and-inspecting/) and the
|
||||
[Querying with joins](/guides/querying-with-joins/) guide.
|
||||
Reference in New Issue
Block a user