# SQL для QA: 8 упражнений с ответами

QA Hub · Версия 1 · 22 октября 2026

Практика фильтрации, JOIN, GROUP BY, HAVING, NULL и проверки качества данных. Откройте одноразовую SQL-сессию и выполните [общий файл данных](../sql-for-qa-fixture.sql). Он создаёт две временные таблицы с вымышленными данными и не изменяет таблицы приложения. Выполняйте подготовку и упражнения в одной сессии. После закрытия сессии временные таблицы исчезнут. Используемый синтаксис поддерживается PostgreSQL и SQLite.

Если скачиваете документ отдельно, также скачайте `https://qahub.pro/resources/sql-for-qa-fixture.sql`.

## Данные и бизнес-правила

В `qa_customers(customer_id, name, email)` записаны Ada (1), Boris (2), Chen (3), Dina (4). У Ada и Chen намеренно одинаковый email; у Dina email равен NULL.

В `qa_orders(order_id, customer_id, status, amount_cents)` пять заказов. Суммы — целое число центов. Заказы 101, 103 и 105 оплачены; 102 отменён; 104 ожидает оплаты. У заказа 105 намеренно нулевая сумма.

В упражнениях сумма оплаченного заказа должна быть положительной, непустые email — уникальными, а отчёт должен включать покупателей без заказов. Это условия учебного продукта; в реальном проекте правила могут отличаться.

## Задания

1. Выведите оплаченные заказы по возрастанию ID.
2. Посчитайте все заказы каждого покупателя, включая покупателей без заказов.
3. Посчитайте оплаченную выручку по покупателям, включая нулевую.
4. Найдите покупателей, которые ни разу не делали заказ.
5. Найдите повторяющиеся email, исключив NULL.
6. Найдите оплаченные заказы с некорректной суммой согласно заданному правилу.
7. Найдите покупателей с двумя и более заказами любого статуса.
8. Найдите покупателей без email.

До просмотра ответа предскажите число строк и конкретные значения. Успешное выполнение запроса не доказывает правильность его логики.

## Ответы и ожидаемые результаты

### 1. Оплаченные заказы
```sql
SELECT order_id, customer_id, amount_cents
FROM qa_orders WHERE status = 'paid'
ORDER BY order_id;
```
Результат: `(101, 1, 1200)`, `(103, 2, 700)`, `(105, 3, 0)`.

### 2. Число заказов, включая ноль
```sql
SELECT c.customer_id, c.name, COUNT(o.order_id) AS order_count
FROM qa_customers c
LEFT JOIN qa_orders o ON o.customer_id = c.customer_id
GROUP BY c.customer_id, c.name
ORDER BY c.customer_id;
```
Результат: `(1, Ada, 2)`, `(2, Boris, 2)`, `(3, Chen, 1)`, `(4, Dina, 0)`.
`COUNT(*)` ошибочно посчитал бы строку Dina без совпадения в LEFT JOIN как один заказ.

### 3. Оплаченная выручка в центах
```sql
SELECT c.customer_id,
       COALESCE(SUM(o.amount_cents), 0) AS paid_cents
FROM qa_customers c
LEFT JOIN qa_orders o
  ON o.customer_id = c.customer_id AND o.status = 'paid'
GROUP BY c.customer_id
ORDER BY c.customer_id;
```
Результат: `(1, 1200)`, `(2, 700)`, `(3, 0)`, `(4, 0)`.
Условие `o.status = 'paid'` в WHERE исключило бы покупателей без оплаченных заказов. Потеря Dina — ошибка отчёта, даже если итоговая выручка остаётся равной 1900 центам.

### 4. Покупатели без заказов
```sql
SELECT c.customer_id, c.name
FROM qa_customers c
WHERE NOT EXISTS (
  SELECT 1 FROM qa_orders o WHERE o.customer_id = c.customer_id
)
ORDER BY c.customer_id;
```
Результат: `(4, Dina)`.

### 5. Повторяющиеся email
```sql
SELECT email, COUNT(*) AS occurrences
FROM qa_customers WHERE email IS NOT NULL
GROUP BY email HAVING COUNT(*) > 1
ORDER BY email;
```
Результат: `(ada@example.invalid, 2)`. Учёт регистра и пробелов требует дополнительного согласованного правила продукта.

### 6. Некорректные суммы оплаченных заказов
```sql
SELECT order_id, amount_cents
FROM qa_orders WHERE status = 'paid' AND amount_cents <= 0
ORDER BY order_id;
```
Результат: `(105, 0)`. Это ошибка именно потому, что условие упражнения запрещает оплаченные заказы нулевой стоимости.

### 7. Два и более заказа
```sql
SELECT customer_id, COUNT(*) AS order_count
FROM qa_orders GROUP BY customer_id
HAVING COUNT(*) >= 2
ORDER BY customer_id;
```
Результат: `(1, 2)`, `(2, 2)`.

### 8. Отсутствующий email
```sql
SELECT customer_id, name FROM qa_customers
WHERE email IS NULL ORDER BY customer_id;
```
Результат: `(4, Dina)`. Условие `email = NULL` не проверяет отсутствие значения.

## Применение в QA

Приложите к дефекту требование, запрос, среду, ожидаемые и фактические строки. До удаления дубликатов выясните, действительно ли они нарушают бизнес-правила. После подготовки данных запросы этих упражнений выполняют только чтение.

Продолжить: https://qahub.pro/ru/backend-api/sql-for-qa
