SQL — один з найбільш недооцінених скілів для QA початківців. Але коли ви вперше зможете самостійно перевірити що дані правильно збереглись в БД після тесту — це змінить підхід до тестування.

Ця стаття — практичний мінімум. Без академічних визначень, з реальними прикладами з QA роботи.

Навіщо QA знати SQL

Верифікація даних. UI показує "замовлення збережено" — але чи правильні дані в базі? Тільки SQL-запит дасть точну відповідь.

Підготовка тест-даних. Потрібно протестити обробку замовлень "старших 30 днів" — простіше вставити тестові дані через SQL ніж створювати їх через UI.

Дебаг багів. "Баг не репродукується" — перевіряємо стан даних в БД і знаходимо причину.

Тестування без UI. Перевірити що бекграунд-процес правильно обробив записи — тільки через БД.

Основи SELECT

1-- Отримати всі записи з таблиці
2SELECT * FROM users;
3
4-- Отримати конкретні поля
5SELECT id, name, email FROM users;
6
7-- З умовою
8SELECT * FROM users WHERE email = 'test@example.com';
9
10-- Кілька умов
11SELECT * FROM users WHERE role = 'admin' AND is_active = true;
12SELECT * FROM orders WHERE status = 'pending' OR status = 'processing';

WHERE: умови відбору

1-- Рівність і нерівність
2WHERE age = 25
3WHERE age != 25  -- або <> 25
4WHERE age > 18
5WHERE age >= 18
6WHERE age BETWEEN 18 AND 65
7
8-- Рядки
9WHERE name = 'Іван'
10WHERE email LIKE '%@gmail.com'    -- закінчується на @gmail.com
11WHERE name LIKE 'Ів%'             -- починається з "Ів"
12WHERE name ILIKE '%іван%'         -- case-insensitive пошук (PostgreSQL)
13
14-- NULL перевірка
15WHERE deleted_at IS NULL          -- активні записи
16WHERE deleted_at IS NOT NULL      -- видалені записи
17
18-- Список значень
19WHERE status IN ('pending', 'processing', 'shipped')
20WHERE id NOT IN (1, 2, 3)
21
22-- Дати
23WHERE created_at >= '2026-01-01'
24WHERE created_at BETWEEN '2026-01-01' AND '2026-12-31'
25WHERE created_at >= NOW() - INTERVAL '7 days'  -- за останні 7 днів

Реальні QA приклади

Перевірити що user зареєструвався

1-- Після реєстрації через UI або API:
2SELECT id, name, email, created_at, is_verified
3FROM users
4WHERE email = 'test@example.com';
5
6-- Що перевіряємо:
7-- ✅ Запис існує
8-- ✅ name і email відповідають введеним даним
9-- ✅ created_at = сьогодні
10-- ✅ is_verified = false (якщо потрібна верифікація email)

Перевірити що замовлення створилось

1SELECT 
2  o.id,
3  o.status,
4  o.total_amount,
5  o.created_at,
6  o.user_id
7FROM orders o
8WHERE o.user_id = 123
9ORDER BY o.created_at DESC
10LIMIT 1;
11
12-- Перевіряємо найновіше замовлення цього user

Перевірити що пароль змінився (без бачення самого пароля)

1-- Запам'ятовуємо хеш ДО зміни
2SELECT password_hash FROM users WHERE id = 456;
3-- '...старий_хеш...'
4
5-- Змінюємо пароль через UI
6
7-- Перевіряємо що хеш змінився
8SELECT password_hash FROM users WHERE id = 456;
9-- '...новий_хеш...' (повинен відрізнятись)

ORDER BY і LIMIT

1-- Сортування за датою (нові спочатку)
2SELECT * FROM orders ORDER BY created_at DESC;
3
4-- Перші 10 записів
5SELECT * FROM users ORDER BY created_at DESC LIMIT 10;
6
7-- Сторінка 2 з 10 записів (пагінація)
8SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 10;
9
10-- Знайти останнє замовлення конкретного user
11SELECT * FROM orders 
12WHERE user_id = 123 
13ORDER BY created_at DESC 
14LIMIT 1;

COUNT, SUM, AVG: агрегатні функції

1-- Кількість users
2SELECT COUNT(*) FROM users;
3
4-- Кількість активних users
5SELECT COUNT(*) FROM users WHERE is_active = true;
6
7-- Загальна сума замовлень
8SELECT SUM(total_amount) FROM orders WHERE status = 'completed';
9
10-- Середній чек
11SELECT AVG(total_amount) FROM orders WHERE status = 'completed';
12
13-- Кількість замовлень по статусам
14SELECT status, COUNT(*) as count
15FROM orders
16GROUP BY status;
17
18-- Результат:
19-- pending     | 45
20-- processing  | 12
21-- completed   | 234
22-- cancelled   | 8

JOIN: об'єднання таблиць

Найважливіша тема для QA. Реальні дані зберігаються в кількох таблицях.

INNER JOIN — тільки ті що є в обох таблицях

1-- Замовлення з іменами users
2SELECT 
3  o.id as order_id,
4  o.total_amount,
5  o.status,
6  u.name as user_name,
7  u.email
8FROM orders o
9INNER JOIN users u ON o.user_id = u.id
10WHERE o.status = 'pending';

LEFT JOIN — всі з лівої таблиці + відповідні з правої

1-- Всі users і кількість їх замовлень (включно з тими хто не замовляв)
2SELECT 
3  u.id,
4  u.name,
5  u.email,
6  COUNT(o.id) as orders_count
7FROM users u
8LEFT JOIN orders o ON u.id = o.user_id
9GROUP BY u.id, u.name, u.email
10ORDER BY orders_count DESC;

Реальний QA приклад: перевірити що order_items правильні

1-- Після створення замовлення з 2 товарами
2SELECT 
3  o.id as order_id,
4  o.total_amount,
5  oi.product_id,
6  oi.quantity,
7  oi.price,
8  p.name as product_name
9FROM orders o
10JOIN order_items oi ON o.id = oi.order_id
11JOIN products p ON oi.product_id = p.id
12WHERE o.id = 789;
13
14-- Перевіряємо:
15-- ✅ Є 2 рядки (2 товари)
16-- ✅ Quantity і price відповідають тому що вибрали
17-- ✅ total_amount = SUM(oi.quantity * oi.price)

Перевірка цілісності даних

1-- Orphan records: order_items без orders (баг!)
2SELECT oi.*
3FROM order_items oi
4LEFT JOIN orders o ON oi.order_id = o.id
5WHERE o.id IS NULL;
6
7-- Users з дублікатами email (якщо такого не повинно бути)
8SELECT email, COUNT(*) as count
9FROM users
10GROUP BY email
11HAVING COUNT(*) > 1;
12
13-- Замовлення з нульовою сумою (підозріло)
14SELECT * FROM orders 
15WHERE total_amount = 0 OR total_amount IS NULL;
16
17-- Рядки в невалідному статусі
18SELECT * FROM orders
19WHERE status NOT IN ('pending', 'processing', 'shipped', 'completed', 'cancelled');

Підготовка тест-даних

1-- Вставка тестового user
2INSERT INTO users (name, email, role, created_at)
3VALUES ('Test User', 'testqa@example.com', 'user', NOW());
4
5-- Масова вставка тестових даних
6INSERT INTO products (name, price, category) VALUES
7  ('Тестовий продукт 1', 100.00, 'electronics'),
8  ('Тестовий продукт 2', 250.00, 'electronics'),
9  ('Тестовий продукт 3', 50.00, 'books');
10
11-- Оновлення для тесту (наприклад, зробити замовлення "старим")
12UPDATE orders
13SET created_at = NOW() - INTERVAL '35 days'
14WHERE id = 789;
15
16-- Видалення тестових даних після тесту
17DELETE FROM users WHERE email LIKE '%testqa%';

⚠️ Завжди робіть INSERT/UPDATE/DELETE тільки в тестовій БД! Перед виконанням перевірте до якої бази підключені.

Транзакції: коли тестуєте критичні операції

1-- Починаємо транзакцію (зміни не застосовуються одразу)
2BEGIN;
3
4-- Робимо тестові зміни
5UPDATE orders SET status = 'cancelled' WHERE id = 789;
6
7-- Перевіряємо результат
8SELECT * FROM orders WHERE id = 789;
9
10-- Якщо все ок — застосовуємо
11COMMIT;
12
13-- Або скасовуємо якщо щось не так
14ROLLBACK;

Практичний чеклист SQL для QA

1Базовий рівень (Junior):
2□ SELECT з WHERE і кількома умовами
3□ ORDER BY і LIMIT
4□ COUNT записів
5□ Перевірка NULL значень
6
7Середній рівень (Middle):
8□ JOIN двох таблиць (INNER і LEFT)
9□ GROUP BY з COUNT/SUM
10□ Пошук дублікатів
11□ Дати і часові відрізки
12
13Практика:
14□ Верифікація даних після кожного API тесту
15□ Пошук orphan records
16□ Перевірка цілісності після batch операцій

Де практикуватись

SQLZoo (sqlzoo.net) — інтерактивні завдання в браузері, безкоштовно.

LeetCode SQL — задачі від простих до складних.

pgexercises.com — PostgreSQL-специфічні вправи.

Реальна практика: попросіть доступ до тестової БД на роботі або встановіть PostgreSQL локально і практикуйтесь на реальних даних.

Підсумок

Для QA не потрібно знати весь SQL на рівні DBA. Потрібно впевнено писати SELECT запити з умовами, JOIN і базовою агрегацією — і це вже відкриє можливість тестувати на рівні даних, а не тільки UI.

Почніть з SELECT і WHERE, за тиждень практики додайте JOIN — і ви вже будете в топ 30% QA за цим скілом.