Базовый синтаксис SELECT * FROM table;

Чуть сложнее: SELECT field_1, field_2 FROM table WHERE condition;

Примеры с JOIN-ом таблиц (найти все имена пользователей, которым Иван отправлял сообщения)

SELECT u2.name 
FROM users u1 
  JOIN messages m
   
 ON m.sender_id = u1.id
  JOIN users u2
    ON m.receiver_id = u2.id
  WHERE u1.name = "Ivan"

Фактически JOIN создаёт в памяти новую таблицу — Декартово произведение исходных таблиц, и фильтрует её по условию, которое указано после слова ON (как правило, там говорится, как склеить одну таблицу с другой; обычно по равенству каких-нибудь идентификаторов типа messages.sender_id = user.id). А потом таблица дополнительно фильтруется условиями WHERE и другими операторами. И в самом конце из получившейся таблицы выбираются только нужные нам колонки. Не нужно беспокоиться, что полученная в результате JOIN таблица, будет большой. База данных сама придумает, как оптимизировать этот запрос и не делать лишней работы. Обратите внимание, что мы пишем FROM <table_name> <alias_name> (например FROM users u1). Теперь таблица имеет «псевдоним» u1 (в рамках нашего запроса). Если одна и та же таблица нам нужна несколько раз, причём нам нужны разные строки (как таблица пользователей в примере про сообщения, которую мы использовали один раз для отправителя, другой — для получателя), мы просто даём два разных псевдонима.

С результирующей таблицей можно проделать разные вещи, прежде чем возвращать её. Например, если нужно, чтобы имена не повторялись, SELECT u2.name меняем на SELECT DISTINCT(u2.name) Или не возвращать таблицу, а только посчитать число строк SELECT COUNT(*). Сумма по столбцу: SELECT SUM(price) итд.

Что ещё может понадобиться: SELECT * FROM table LIMIT 5 — взять первые 5 строк получившейся таблицы

SELECT * FROM messages ORDER BY date ASC — отсортировать сообщения по возрастанию (ascend) даты. Бывает ещё DESC (descend), который сортирует по убыванию

Часто эти модификаторы объединяют. Например, чтобы найти самый дорогой предмет, мы отсортируем по цене по убыванию и возьмём одну верхнюю строку: SELECT * FROM items ORDER BY price DESC LIMIT 1

Бывает ещё так, что нам нужно взять сто элементов, но не первые, а например третьи, т.е. строки 200–300 (для того, чтобы разбить данные на страницы по 100 строк). Для этого есть «сдвиг», OFFSET: SELECT * FROM messages ORDER BY date ASC LIMIT 100 OFFSET 200 Но если вы делаете OFFSET обязательно нужно сначала отсортировать, т.к. в разных запросах порядок может быть разный.

Условия можно комбинировать при помощи логических операторов WHERE id = 42 or id = 137 или WHERE sender_id = 1 AND date = "2021-12-13"

Есть операторы сравнения: WHERE price < 100 WHERE age BETWEEN 14 AND 18

Можно проверить, что элемент — один из списка WHERE id IN (1,2,3)

Можно проверить, что строка подходит под шаблон name LIKE "Ivan %"

Сгруппировать строки таблицы по какому-то полю (после этого работать можно только с группами, не с отдельными строками) GROUP BY field Например, сколько сообщений отправил каждый пользователь за последний год:

SELECT u.name, COUNT(*) AS num_messages
FROM users u JOIN messages m ON m.sender_id = u.id
WHERE m.date BETWEEN '2020-12-13' AND '2021-12-13'
GROUP BY u.name

Есть ещё более хитрые штуки. Например, условие на то, что в группе не меньше 3 элементов пишется после GROUP BY так: GROUP BY u.id HAVING COUNT(*) > n HAVING — это такой WHERE, но не для отдельных строк, а для групп.