Перейти к основному содержимому

SQL — реальные кейсы

Подборка готовых SQL-запросов с построчным разбором — как в галерее Turtle: скопировали код, выполнили, сверили результат с таблицей ниже. Схема — учебный интернет-магазин (клиенты, товары, заказы, позиции чека).

Материал заточен под запросы вроде «sql примеры join», «sql group by sum», «как посчитать сумму заказа sql», «sql выборка where», «лабораторная работа база данных sql», «sql insert update пример» — когда нужен рабочий текст запроса и объяснение, а не только синтаксис из учебника.


Для кого эта статья

АудиторияЗачем открыть
ШкольникиИнформатика, лабораторная «таблицы и запросы», подготовка к заданиям с базами
СтудентыКурсовой фрагмент с SELECT/JOIN, отчёт с скриншотами из DBeaver или тренажёра
СамоучкиПервый SQL после Excel — те же отчёты, но языком запросов
Начинающие разработчикиBackend + PostgreSQL, типовые отчёты для админки
АналитикиБыстрый прототип выгрузки перед Pandas

Как работать с примером (как с кодом Turtle):

  1. Откройте схему и демо-таблицы — без них цифры в ответе «не сойдутся».
  2. Скопируйте SQL из блока в тренажёр на этой странице (кнопка «Выполнить») или в psql / DBeaver.
  3. Сравните вывод с колонкой «Пример результата» в разделе.
  4. Прочитайте таблицу «Разбор по строкам» — что делает каждая строка.
  5. Сделайте «Попробуйте» — одно изменение (WHERE, JOIN) закрепляет тему лучше, чем перечитывание.
Сначала теория

Маршрут с нуля — SQL — о разделе и первые шаги. Тот же тренажёр в теории — SQL — язык. Шпаргалка без разбора — типичные задачи. Таблицы в Python — Pandas, формулы — Excel. PostgreSQL в Docker — compose-стеки. Минимальный CRUD — шаблоны.

Загрузка SQL-тренажёра…

Над редактором — чипы-примеры (SELECT, JOIN, GROUP BY, INSERT…). Нажмите чип, затем разберите тот же текст по таблице в статье.


Краткий указатель — что ищут в Google

РазделТипичный запрос (RU / EN)
Как читать запроскак читать sql запрос, sql синтаксис для начинающих
Схема магазинаsql схема базы данных пример, foreign key sql
Каркас SELECTsql select from where, структура sql запроса
Стартовые выборкиsql выборка where, sql сортировка order by
1. Сумма заказаsql sum join, сумма заказа sql, group by sql пример
2. Топ товаровsql топ товаров, count group by
3. Клиенты без заказовleft join sql пример, клиенты без заказов sql
4. Складsql where остаток, выборка товаров sql
5. Статусыsql count group by, средний чек sql
6. JOIN справочникsql inner join две таблицы, sql join example
7. DMLsql insert пример, sql update set
8. Ошибкиsql group by error, ambiguous column sql

Как читать SQL-запрос по частям

Любой запрос читают сверху вниз как инструкцию для СУБД. Сначала «откуда взять строки», потом «что отфильтровать», потом «что показать».

Пример одной фразы:

SELECT name FROM products WHERE price > 500;
ЧастьРольАналог в Excel
SELECT nameКакие столбцы попадут в ответВыделили столбец «Название»
FROM productsИз какой таблицыЛист «Товары»
WHERE price > 500Какие строки оставитьАвтофильтр «цена > 500»
;Конец команды (в тренажёре можно без неё)

Типы команд в этой статье:

КомандаЗачемМеняет данные?
SELECTПрочитать, посчитать отчётНет
INSERTДобавить строкиДа
UPDATEИзменить строкиДа
DELETEУдалить строкиДа

Схема учебного магазина

Четыре таблицы — типичный интернет-магазин. Одна строка в order_items — одна позиция в чеке (как строка в кассовом чеке).

ТаблицаРольКлючевые столбцы
customersПокупателиcustomer_id, full_name, email, city
productsКаталогproduct_id, name, price, category, stock_qty
ordersЗаказorder_id, customer_id, order_date, status
order_itemsПозиция чекаorder_id, product_id, quantity, unit_price

Зачем unit_price в позиции: в чеке фиксируют цену на момент покупки. Поле products.price могли изменить завтра — сумма старого заказа от этого не меняется.

В тренажёре также есть users и citiesраздел 6.

На PostgreSQL те же таблицы в схеме shop_data — пишут shop_data.customers. В браузерном тренажёре префикс не нужен.

Демо-данные — сверяйте с ними ответы

Выполните в тренажёре SELECT * FROM products; — увидите те же строки.

products (фрагмент):

product_idnamepricecategorystock_qty
1Алгоритмы799Книги15
2SQL для профессионалов1299Книги0
3USB-кабель299Аксессуары42
5Блокнот99Канцелярия120

orders:

order_idcustomer_idorder_datestatus
11 (Анна)2024-05-01completed
21 (Анна)2024-05-10new
32 (Борис)2024-05-12processing
43 (Елена)2024-05-15completed

order_items — отсюда берутся суммы в примерах:

order_idproduct_idquantityunit_priceСтрока чека
111799799
132299598
22112991299
34114991499
45399297

Суммы заказов вручную (для проверки GROUP BY):

order_idРасчётorder_total
1799 + 2×2991397
212991299
314991499
43×99297

Основы — от задачи к запросу

Перед кодом сформулируйте задачу словами:

  1. Какие таблицы нужны? (клиенты, заказы, позиции…)
  2. Как они связаны? (customer_id, order_id…)
  3. Какой фильтр? (статус, дата, город…)
  4. Что в ответе? (список имён, сумма, количество…)
  5. Сортировка? (сначала самые дорогие, старые заказы первыми…)

На лабораторной и собеседовании эту цепочку часто просят проговорить до написания SQL.


Основы SELECT

Обязательный каркас

Минимальная программа в мире SQL — один SELECT. Полный «скелет» отчёта:

SELECT столбцы_или_выражения -- ЧТО показать
FROM таблица -- ОТКУДА строки
JOIN другая ON связь -- ДОБАВИТЬ связанные строки
WHERE условие_на_строки -- ФИЛЬТР до группировки
GROUP BY столбцы_группы -- СВЕРНУТЬ в группы
HAVING условие_на_группы -- ФИЛЬТР после группировки
ORDER BY сортировка -- ПОРЯДОК строк в ответе
LIMIT число; -- СКОЛЬКО строк вернуть

Порядок выполнения СУБД (упрощённо — для понимания, не для заучивания порядка написания):

ШагКлаузаЧто происходит
1FROM / JOINСклеивают таблицы; получается длинный список строк
2WHEREОтбрасывают лишние строки
3GROUP BYОбъединяют строки в группы (например, по order_id)
4HAVINGОтбрасывают целые группы
5SELECTСчитают SUM, COUNT, выводят столбцы
6ORDER BY / LIMITСортируют и обрезают результат

Теория — принципы SQL-движка.


Шаблон — клиент и его завершённые заказы

Задача: список имён и дат только по заказам со статусом completed.

SELECT
c.full_name,
o.order_id,
o.order_date
FROM customers c
INNER JOIN orders o ON o.customer_id = c.customer_id
WHERE o.status = 'completed'
ORDER BY o.order_date DESC;

Разбор по строкам:

СтрокаКодСмысл
1–4SELECT c.full_name, o.order_id, o.order_dateТри столбца в результате; префикс c. / o. — из какой таблицы столбец
5FROM customers cГлавная таблица — клиенты; c — короткий псевдоним
6INNER JOIN orders o ON o.customer_id = c.customer_idК каждому клиенту подтянуть только его заказы по совпадению id
7WHERE o.status = 'completed'Оставить строки, где статус заказа — завершён
8ORDER BY o.order_date DESCСначала свежие даты (DESC — по убыванию)

Пример результата (смысл):

full_nameorder_idorder_date
Анна Иванова12024-05-01
Елена Смирнова42024-05-15

Заказ Анны №2 (new) и заказ Бориса не попадут — фильтр completed.

Частая ошибка: писать status = completed без кавычек — СУБД воспримет completed как имя столбца, будет ошибка.

Попробуйте: замените INNER JOIN на LEFT JOIN — появятся ли клиенты без заказов? (да, с NULL в столбцах заказа).


Стартовые выборки

Простые запросы без JOIN — как первый квадрат в Turtle: одна таблица, один навык.


Все товары — проверка связи с БД

Задача: убедиться, что запрос выполняется (аналог print("Hello")).

SELECT * FROM products;
СтрокаСмысл
SELECT *Все столбцы всех строк
FROM productsТаблица каталога

Попробуйте: SELECT name, price FROM products; — только два столбца.


Каталог дороже 500 ₽

Задача: витрина «дорогие товары» для сайта.

SELECT product_id, name, price, category
FROM products
WHERE price > 500
ORDER BY price DESC;

Разбор по строкам:

СтрокаКодСмысл
1SELECT product_id, name, price, categoryЯвный список столбцов — лучше, чем *, в реальных проектах
2FROM productsИсточник — каталог
3WHERE price > 500Число без кавычек; сравнение столбца с константой
4ORDER BY price DESCСортировка по убыванию цены

Пример результата:

product_idnamepricecategory
2SQL для профессионалов1299Книги
4Power bank1499Аксессуары
1Алгоритмы799Книги

Блокнот (99) и USB-кабель (299) отфильтрованы условием > 500.

Попробуйте: AND category = 'Книги' в той же строке WHERE.


Клиенты из Москвы

Задача: выгрузка для региональной рассылки.

SELECT full_name, email, city
FROM customers
WHERE city = 'Москва';

Разбор:

СтрокаСмысл
WHERE city = 'Москва'Текст в одинарных кавычках; пробел и регистр должны совпасть с данными
Без JOINОдна таблица — проще всего для первой лабораторной

Пример результата: Анна Иванова и Елена Смирнова (в демо у обеих город Москва).

Частая ошибка: WHERE city = Москва — СУБД ищет столбец с именем Москва.


Заказы за май 2024

Задача: отчёт за календарный месяц.

SELECT order_id, customer_id, order_date, status
FROM orders
WHERE order_date >= '2024-05-01'
AND order_date < '2024-06-01';

Разбор:

СтрокаСмысл
>= '2024-05-01'С 1 мая включительно
AND order_date < '2024-06-01'До 1 июня не включая — весь май
ANDОба условия должны выполняться

Так задают диапазон дат в SQL чаще, чем BETWEENBETWEEN легко ошибиться с последним днём месяца).

Пример результата: все четыре демо-заказа (даты с 2024-05-01 по 2024-05-15).

Попробуйте: оставьте только status = 'new' — останется заказ №2.


Поиск по подстроке в названии (LIKE)

Задача: найти товары со словом «кабель» в имени.

SELECT name, price
FROM products
WHERE name LIKE '%кабель%';
ЧастьСмысл
LIKEСравнение с шаблоном, не с точным равенством
%Любое количество любых символов до и после
'%кабель%'«кабель» где угодно внутри строки

Пример результата: USB-кабель.

В PostgreSQL для регистронезависимого поиска — ILIKE '%кабель%'.


Примеры из практики

Кейсы ниже — типичные задания лабораторных, стажировок и джуниор-позиций. Каждый блок: задача → код → разбор по строкам → пример результата → попробуйте.

Все запросы проверены в тренажёре на этой странице.


1. Выручка и сумма заказа

1.1. Сумма каждого заказа (GROUP BY + SUM + JOIN)

Задача: для каждого order_id посчитать итог чека — сумма quantity * unit_price по всем позициям.

Какие таблицы: orders + order_items (без клиентов — только чеки).

SELECT
o.order_id,
o.order_date,
o.status,
SUM(oi.quantity * oi.unit_price) AS order_total
FROM orders o
INNER JOIN order_items oi ON oi.order_id = o.order_id
GROUP BY o.order_id, o.order_date, o.status
ORDER BY order_total DESC;

Разбор по строкам:

СтрокаКодСмысл
1–5SELECT … SUM(oi.quantity * oi.unit_price) AS order_totalДля каждой группы сложить произведения «кол-во × цена в чеке»
5SUM(...)Агрегатная функция — одно число на группу
5AS order_totalИмя столбца в результате (удобно в отчёте и API)
6FROM orders oЗаказы — «хребет» отчёта
7INNER JOIN order_items oi ON oi.order_id = o.order_idПриклеить строки чека к заказу
8GROUP BY o.order_id, o.order_date, o.statusОдна группа = один заказ; в PostgreSQL все неагрегированные столбцы из SELECT должны быть здесь
9ORDER BY order_total DESCСначала самые крупные чеки

Почему нельзя забыть GROUP BY: без него SUM попытается сложить все позиции всех заказов в одно число.

Пример результата (сверка с таблицей сумм выше):

order_idorder_datestatusorder_total
12024-05-01completed1397
32024-05-12processing1499
22024-05-10new1299
42024-05-15completed297

Порядок строк после ORDER BY order_total DESC именно такой (1397 сверху).

Связь с Excel: на листе «Позиции» столбец «Сумма строки» = quantity * price, затем сводная таблица по order_id с полем «Сумма». В Pandas — df.groupby('order_id').apply(...), см. 1113.

Попробуйте: добавьте WHERE o.status = 'completed' перед GROUP BY — останутся только заказы 1 и 4.


1.2. Выручка по клиентам за всё время

Задача: кто сколько потратил — для CRM, бонусов, «топ покупателей».

Какие таблицы: цепочка customersordersorder_items.

SELECT
c.customer_id,
c.full_name,
SUM(oi.quantity * oi.unit_price) AS revenue
FROM customers c
INNER JOIN orders o ON o.customer_id = c.customer_id
INNER JOIN order_items oi ON oi.order_id = o.order_id
GROUP BY c.customer_id, c.full_name
ORDER BY revenue DESC;

Разбор по строкам:

СтрокаСмысл
FROM customers cСтарт с покупателей — в отчёте будет каждый клиент, у кого есть хотя бы одна позиция в заказе
Первый JOIN ordersПодтянуть заказы клиента
Второй JOIN order_itemsПодтянуть позиции этих заказов
GROUP BY c.customer_id, c.full_nameСвернуть все позиции всех заказов клиента в одну сумму

Пример результата (ручная проверка):

full_namerevenueОткуда цифра
Анна Иванова2696заказ 1 (1397) + заказ 2 (1299)
Борис Петров1499заказ 3
Елена Смирнова297заказ 4

Клиент Дмитрий Козлов (без заказов в демо) не появится — сработал INNER JOIN.

Попробуйте: LEFT JOIN вместо первого JOIN с orders и фильтр — отдельный кейс «Клиенты без заказов».


2. Топ товаров по продажам

2.1. Сколько штук продали (не рубли)

Задача: рейтинг товаров по количеству проданных единиц.

SELECT
p.product_id,
p.name,
SUM(oi.quantity) AS units_sold
FROM products p
INNER JOIN order_items oi ON oi.product_id = p.product_id
GROUP BY p.product_id, p.name
ORDER BY units_sold DESC;

Разбор:

СтрокаСмысл
SUM(oi.quantity)Складываем штуки, а не деньги
INNER JOINТовары без продаж в отчёт не попадут

Пример результата:

nameunits_sold
Блокнот3
USB-кабель2
Алгоритмы, SQL…, Power bankпо 1

2.2. Товары, которые ни разу не продавали

Задача: кандидаты на распродажу или снятие с витрины.

SELECT
p.product_id,
p.name,
p.stock_qty
FROM products p
LEFT JOIN order_items oi ON oi.product_id = p.product_id
WHERE oi.order_item_id IS NULL;

Разбор по строкам:

СтрокаСмысл
LEFT JOINВзять все товары из products
WHERE oi.order_item_id IS NULLОставить только те, для кого не нашлось строки в order_items
Проверка по order_item_idЛюбой столбец из правой таблицы будет NULL, если совпадения не было

В текущих демо-данных все 5 товаров хотя бы раз продавались — результат пустой. Это нормально: запрос верный, данных под условие нет.

Попробуйте: выполните INSERT нового товара без заказов (раздел 7) и повторите запрос.


2.3. Топ с нулевыми продажами (LEFT JOIN + COALESCE)

Задача: в одной таблице — и проданные, и непроданные; у непроданных — 0.

SELECT
p.name,
COALESCE(SUM(oi.quantity), 0) AS units_sold
FROM products p
LEFT JOIN order_items oi ON oi.product_id = p.product_id
GROUP BY p.product_id, p.name
ORDER BY units_sold DESC;
ЧастьСмысл
LEFT JOINВсе товары остаются в группах
COALESCE(SUM(...), 0)SUM по пустой группе даёт NULL — заменяем на 0

3. Клиенты без заказов

3.1. LEFT JOIN + IS NULL

Задача: кому отправить письмо «вы забыли корзину».

SELECT c.customer_id, c.full_name, c.email
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL;

Разбор:

СтрокаСмысл
LEFT JOIN ordersВсе клиенты + их заказы, если есть
WHERE o.order_id IS NULLСтроки, где заказ не подтянулся — у клиента 0 заказов

Попробуйте: INSERT INTO customers ... без заказов для нового id — увидите строку в результате.


3.2. NOT EXISTS (часто на экзаменах)

SELECT c.customer_id, c.full_name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
ЧастьСмысл
EXISTS (подзапрос)Истина, если подзапрос вернул хотя бы одну строку
NOT EXISTSИстина, если заказов нет
SELECT 1В подзапросе важен факт наличия строки, а не значение столбца

Теория — подзапросы и EXISTS.


3.3. Email для рассылки

SELECT full_name, email
FROM customers
WHERE email IS NOT NULL;

Разбор:

НеправильноПравильно
email = NULLemail IS NULL
email != NULLemail IS NOT NULL

Пример: Дмитрий Козлов с email = NULL не попадёт в рассылку.


4. Склад — нулевой и низкий остаток

4.1. Нет на складе

SELECT product_id, name, category, stock_qty
FROM products
WHERE stock_qty = 0
ORDER BY category, name;

Пример результата: «SQL для профессионалов», stock_qty = 0.


4.2. Мало на складе (меньше 10, но больше 0)

SELECT name, category, stock_qty
FROM products
WHERE stock_qty > 0 AND stock_qty < 10
ORDER BY stock_qty ASC;

Разбор: AND объединяет два условия; ORDER BY stock_qty ASC — сначала критичные остатки (Power bank, 8 шт.).


5. Заказы по статусу

5.1. Сколько заказов в каждом статусе

SELECT status, COUNT(*) AS cnt
FROM orders
GROUP BY status
ORDER BY cnt DESC;

Разбор:

СтрокаСмысл
COUNT(*)Число строк в группе = число заказов
GROUP BY statusГруппа = одно значение статуса

Пример результата: по одному заказу в статусах completed, new, processing (cnt = 1), если в демо по одному заказу каждого типа.


5.2. Очередь «новых» заказов (FIFO)

SELECT o.order_id, c.full_name, o.order_date
FROM orders o
INNER JOIN customers c ON c.customer_id = o.customer_id
WHERE o.status = 'new'
ORDER BY o.order_date ASC;

Пример: заказ №2, Анна Иванова, 2024-05-10.


5.3. Средний чек по завершённым заказам

Задача: одно число — средняя сумма завершённого чека.

Идея в два шага: (1) посчитать сумму каждого заказа; (2) взять среднее от этих сумм.

SELECT AVG(order_total) AS avg_check
FROM (
SELECT
o.order_id,
SUM(oi.quantity * oi.unit_price) AS order_total
FROM orders o
INNER JOIN order_items oi ON oi.order_id = o.order_id
WHERE o.status = 'completed'
GROUP BY o.order_id
) t;

Разбор:

УровеньСмысл
Внутренний SELECTТаблица из двух строк: заказы 1 (1397) и 4 (297)
WHERE o.status = 'completed'Только завершённые до группировки
Внешний AVG(order_total)(1397 + 297) / 2 = 847
tПсевдоним подзапроса в FROM — обязателен

В PostgreSQL то же читается проще с WITHCTE.


6. Сотрудники и справочник городов

Учебный кейс «факт + справочник» — как orders + customers, но про HR.

6.1. INNER JOIN — только с заполненным городом

SELECT u.name AS employee, c.name AS city_name, c.region
FROM users u
INNER JOIN cities c ON u.city_id = c.id
ORDER BY c.region, u.name;

Разбор:

СтрокаСмысл
u.city_id = c.idУсловие связи: внешний ключ → первичный ключ справочника
AS employeeПсевдоним столбца в результате
u, cПсевдонимы таблиц

6.2. LEFT JOIN — показать всех, даже без города

SELECT u.name, c.name AS city_name
FROM users u
LEFT JOIN cities c ON u.city_id = c.id;

Если у сотрудника city_id пустой — в city_name будет NULL.


6.3. UNION — список городов из двух источников

SELECT city AS place FROM customers
UNION
SELECT city AS place FROM users;
КомандаСмысл
UNIONОбъединить результаты и убрать дубликаты
UNION ALLОбъединить с дубликатами (быстрее, если дубли допустимы)
Одинаковые имена столбцовВ обеих частях первый столбец логически «место»

7. Изменение данных (DML)

В тренажёре команды меняют демо-базу до кнопки «Сброс». На сервере используют транзакции.

7.1. Пополнить склад

UPDATE products
SET stock_qty = stock_qty + 50
WHERE category = 'Книги';

Разбор по строкам:

СтрокаСмысл
UPDATE productsКакую таблицу меняем
SET stock_qty = stock_qty + 50Новое значение = старое + 50 (атомарно на сервере)
WHERE category = 'Книги'Только книги; без WHERE обновятся все строки

После выполнения у «SQL для профессионалов» остаток станет 50 (был 0).


7.2. Новый товар

INSERT INTO products (name, price, category, stock_qty)
VALUES ('Наушники', 2499, 'Аксессуары', 10);
СтрокаСмысл
Список в скобках после productsВ какие столбцы пишем (остальные — default или NULL)
VALUES (...)Одна новая строка

Проверка:

SELECT * FROM products WHERE name = 'Наушники';

7.3. Сменить статус заказов

UPDATE orders
SET status = 'cancelled'
WHERE status = 'new';

Пример: отменится заказ №2 (единственный new в демо).


7.4. Удаление (осторожно)

DELETE FROM orders WHERE status = 'new';
DELETE без WHERE

DELETE FROM orders удалит все заказы. В тренажёре — «Сброс»; на сервере — бэкап, см. резервное копирование.


7.5. Безопасный ввод из приложения (SQL-инъекции)

Нельзя собирать запрос так:

# ОПАСНО — так не делают
query = f"SELECT * FROM customers WHERE email = '{user_input}'"

Нужно передавать значения отдельно:

cur.execute(
"SELECT * FROM customers WHERE email = %s",
(user_email,),
)
await pool.query('SELECT * FROM customers WHERE email = $1', [userEmail]);

СУБД отделит код запроса от данных. Подробнее — SQL-инъекции.


8. Частые ошибки

СимптомПричинаЧто сделать
column must appear in GROUP BYВ SELECT столбец без агрегата, его нет в GROUP BYДобавить в GROUP BY или MAX(столбец)
ambiguous columnid есть в двух таблицахПисать c.customer_id, o.order_id
Пустой отчёт при JOINСтоит INNER вместо LEFTПоменять тип JOIN или проверить ключи в ON
email = NULL не работаетСравнение с NULLТолько IS NULL / IS NOT NULL
Одна гигантская суммаЗабыли GROUP BY order_idГруппировать по заказу
Строк дублируется в 2×Лишний JOINУбрать лишнюю таблицу или уточнить ON
В тренажёре ошибка на WITHДвижок тренажёра проще сервераВыполнить в PostgreSQL — 111
Цифры не как в статьеДанные меняли INSERT/DELETE«Сброс» в тренажёре

Шпаргалка одной страницей

НужноШаблон
Все строки таблицыSELECT * FROM имя;
ФильтрWHERE столбец = значение
Диапазон датWHERE d >= '2024-05-01' AND d < '2024-06-01'
Связать 2 таблицыFROM a JOIN b ON a.id = b.a_id
Сумма по группамSELECT ключ, SUM(…) FROM … GROUP BY ключ
Клиенты без заказовLEFT JOIN + WHERE заказ.id IS NULL
Добавить строкуINSERT INTO t (cols) VALUES (…);
ИзменитьUPDATE t SET col = … WHERE …;

Что дальше

ТемаКуда идти
JOIN, CTE, окнадорожная карта SQL
Тот же магазин на серверепрактикум shop_data
Рецепты без построчного разборашпаргалка 885
PostgreSQL в Dockercompose-стеки
Таблицы в PythonPandas — примеры
Визуальные примеры кодаTurtle

Скопируйте один кейс, выполните в тренажёре, измените одно условие в WHERE — так SQL запоминается так же, как короткие программы на Turtle: руками, а не только чтением.

См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").