sql.md

SQL #

A query language for relational data. Keywords are case-insensitive; the examples use uppercase for keywords and lowercase for identifiers. Syntax leans on the standard, with notes where PostgreSQL and others differ.

Querying #

SELECT id, name, email          -- pick columns
FROM users
WHERE active = true
ORDER BY name ASC               -- ASC is the default; DESC reverses
LIMIT 20 OFFSET 40;             -- page 3 of 20 (MySQL/Postgres syntax)

SELECT DISTINCT country FROM users;   -- unique values
SELECT *, now() AS fetched_at FROM users;

Filtering #

WHERE age >= 18 AND country IN ('US', 'CA')
WHERE name LIKE 'A%'             -- starts with A   (_ matches one char)
WHERE created_at BETWEEN '2026-01-01' AND '2026-06-30'
WHERE deleted_at IS NULL        -- never = NULL; NULL is not a value
WHERE email IS NOT NULL AND score > (SELECT avg(score) FROM users);

Joins #

SELECT o.id, u.name, o.total
FROM orders o
JOIN users u   ON u.id = o.user_id        -- INNER: matches in both
LEFT JOIN coupons c ON c.order_id = o.id; -- keep all orders, NULL if no coupon

-- INNER  keeps rows with a match on both sides
-- LEFT   keeps all left rows;  RIGHT keeps all right rows
-- FULL   keeps everything;     CROSS is every combination

Aggregation #

SELECT country,
       count(*)        AS users,
       avg(score)      AS avg_score,
       max(created_at) AS newest
FROM users
GROUP BY country                -- one row per country
HAVING count(*) > 10            -- filter groups (WHERE filters rows)
ORDER BY users DESC;

Subqueries & CTEs #

-- Common table expression: name a query, then use it like a table
WITH recent AS (
  SELECT user_id, count(*) AS n
  FROM orders
  WHERE created_at > now() - interval '30 days'
  GROUP BY user_id
)
SELECT u.name, recent.n
FROM recent
JOIN users u ON u.id = recent.user_id
WHERE recent.n > 5;

Window functions #

SELECT name, country, score,
       rank()       OVER (PARTITION BY country ORDER BY score DESC) AS rank_in_country,
       avg(score)   OVER (PARTITION BY country) AS country_avg,
       sum(score)   OVER (ORDER BY created_at)  AS running_total
FROM users;
-- Window functions compute across rows without collapsing them like GROUP BY.

Inserting, updating, deleting #

INSERT INTO users (name, email) VALUES ('Turtle', 't@x.io');
INSERT INTO users (name, email)
  VALUES ('A', 'a@x.io'), ('B', 'b@x.io');   -- multiple rows

UPDATE users SET active = false WHERE last_seen < '2026-01-01';
DELETE FROM users WHERE active = false;

-- Always test a destructive WHERE as a SELECT first.
INSERT INTO users (email) VALUES ('t@x.io')
  ON CONFLICT (email) DO UPDATE SET name = excluded.name;   -- upsert (Postgres)

Schema #

CREATE TABLE users (
  id         serial PRIMARY KEY,
  email      varchar(255) UNIQUE NOT NULL,
  name       text,
  score      numeric DEFAULT 0,
  created_at timestamp DEFAULT now()
);

ALTER TABLE users ADD COLUMN country varchar(2);
ALTER TABLE users DROP COLUMN score;
CREATE INDEX idx_users_email ON users (email);   -- speed up lookups
DROP TABLE users;

Set operations & transactions #

SELECT email FROM users
UNION              -- combine and dedupe;  UNION ALL keeps duplicates
SELECT email FROM subscribers;

BEGIN;
  UPDATE accounts SET balance = balance - 100 WHERE id = 1;
  UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;            -- ROLLBACK to undo everything since BEGIN

© 2026 anguishedturtle.comBit Night RunnerSupportPrivacy