THREADQA
    THREADQA
    Главная
    Курсы
    Java QA AutomationNEW
    Новый курс · уже можно купить
    Python QA Automation
    Pytest, Playwright, Docker
    iOS QA Automation
    XCTest, XCUITest, Fastlane
    Все курсы
    Практика
    Мок собеседование
    Тренировка перед реальным интервью
    Записи собеседований
    Разбор реальных собеседований
    Буткемп
    Интенсивная подготовка к работе
    XPath Practice Hub
    Тренажёр XPath-запросов
    Roadmap
    Путь QA-инженера
    XPath Dinner
    Практика XPath в игровом формате
    Блог
    FAQ
    Для компаний
    1. Домой
    2. Обучение
    3. SQL для тестировщика: 20 запросов на реальной БД ShawarmaShop
    Все статьи
    Обучение
    15 августа 2026 г. 28 мин чтения

    SQL для тестировщика: 20 запросов на реальной БД ShawarmaShop

    SQL для Manual QA на реальной схеме ShawarmaShop: orders, payments, recipes, ingredients, JOIN, проверки API→DB, идемпотентность и переход к PostgreSQL/JDBC в Java автотестах.

    Олег Пендрак
    Олег Пендрак
    Tech Lead QA Automation · Ozon, VK

    SQL тестировщику нужен не ради строчки в резюме. Он нужен в тот момент, когда UI показал «заказ создан», API вернул 200, а тебе надо доказать, что backend действительно сохранил правильные данные — без дублей, неправильной суммы и сломанного статуса.

    В этой статье не будет выдуманной учебной базы. Ниже используется реальная схема PostgreSQL учебного проекта ShawarmaShop от ThreadQA: recipes, ingredients, recipe_ingredients, orders, payments, restock_invoices, event_log и users.

    Начинаем с пользовательского сценария: создаём заказ в ShawarmaShop, а затем проверяем тот же объект через API и PostgreSQL.
    • Открыть учебный стенд ShawarmaShop

      Реальный интерфейс приложения для практики UI и поиска запросов в DevTools.

    • Открыть Swagger UI ShawarmaShop

      Контракт REST API, request/response schemas и endpoints.

    Для QA безопасная база — read-only доступ. SELECT почти всегда достаточно, чтобы проверить persistence. UPDATE/DELETE в общей среде выполняй только по согласованному сценарию и никогда не используй production как песочницу.

    Реальная схема ShawarmaShop: что с чем связано

    Сначала не SQL, а модель данных. Если не понимаешь связи таблиц, даже синтаксически правильный JOIN легко даст ложный результат.

    ТаблицаЧто хранитКлючевые поля для QA
    recipesКаталог шаурмыid, name, size, price, prep_seconds, created_by, updated_by
    ingredientsСкладid, name, on_hand, min_level, supplier_channel
    recipe_ingredientsСвязь рецепт ↔ ингредиентrecipe_id, ingredient_id, qty_needed
    ordersЗаказы и их lifecyclerecipe_id, qty, total_price, status, placed_at, paid_at, completed_at
    paymentsПлатежиorder_id, idempotency_key, amount, method, txn_id, status
    restock_invoicesИнвойсы пополненияingredient_id, invoice_number, total_cost, status, eta
    event_logRead-модель Kafka eventstype, payload jsonb, ts
    usersУчётные записиusername, password_hash, created_at
    text
    1recipes 1 ───── * orders
    2recipes 1 ───── * recipe_ingredients * ───── 1 ingredients
    3orders  1 ───── * payments
    4ingredients 1 ─ * restock_invoices
    5event_log: отдельная read-модель событий Kafka (payload JSONB)
    6users: учётки; demo-модель single-user
    Реальная схема ShawarmaShop. Для большинства ежедневных QA-проверок достаточно понимать связи orders → recipes и orders → payments, а затем постепенно подключать склад и события.

    Жизненный цикл заказа

    text
    1PENDING → PAID → PREPARING → DONE
    2    └──────────────→ CANCELLED (по разрешённому бизнес-сценарию)
    3
    4orders.paid_at       = NULL до успешной оплаты
    5orders.completed_at  = NULL до DONE
    6orders.bread_batch_id = NULL до брони лаваша через gRPC-пекарню

    Это уже готовый набор инвариантов для тестирования: у PENDING не должно быть completed_at, DONE должен иметь completed_at, а payment и order должны согласовываться между собой.

    API → DB: как связать проверку

    POST /api/v1/orders возвращает OrderResponse с id, status, recipe, qty, totalPrice, placedAt, etaAt и payment. Самый удобный correlation key — order id: его забираем из API и используем в SELECT.

    ID из API-ответа используем как correlation key: UI → API → orders → payments → event_log.
    text
    11. POST /api/v1/orders
    22. HTTP 200
    33. response.id = 7812
    44. response.status = PENDING
    55. SELECT ... FROM orders WHERE id = 7812
    66. сравнить recipe_id / qty / total_price / status
    MANUAL QA → AUTOMATION

    Уже умеешь проверять это руками? Следующий шаг — автоматизировать

    На Java QA Automation знакомые ручные проверки превращаются в код: Postman и HTTP — в REST Assured, UI и локаторы — в Selenide, SQL — в PostgreSQL/JDBC. Ты не начинаешь профессию заново, а переносишь уже знакомую логику тестирования в автотесты.

    Java 21 · REST Assured · Selenide · Kafka
    Письменная проверка практических заданий
    ShawarmaShop · Docker · CI/CD · карьерный блок
    Посмотреть программу Java QAJava QA с нуля: полный roadmapСтоимость курса: 65 000 ₽

    20 SQL-запросов, которые реально пригодятся QA

    1. Найти заказ по ID из API response

    sql
    1SELECT *
    2FROM orders
    3WHERE id = 7812;

    Это базовая сквозная проверка после POST /api/v1/orders или GET /api/v1/orders/{id}.

    2. Выбрать только поля, которые сравниваем с API

    sql
    1SELECT id, recipe_id, qty, total_price, status, placed_at, eta_at
    2FROM orders
    3WHERE id = 7812;

    После создания заказа ожидаем PENDING. qty и total_price должны согласовываться с OrderResponse.qty и OrderResponse.totalPrice.

    3. Проверить lifecycle timestamps заказа

    sql
    1SELECT id, status, placed_at, paid_at, completed_at, bread_batch_id
    2FROM orders
    3WHERE id = 7812;

    Для PENDING логично ожидать paid_at/completed_at = NULL. После успешной оплаты paid_at должен появиться; после DONE — completed_at. Конкретные переходы проверяй против требований проекта.

    4. Последние заказы

    sql
    1SELECT id, recipe_id, qty, total_price, status, placed_at
    2FROM orders
    3ORDER BY placed_at DESC
    4LIMIT 10;

    На общем стенде не используй «самый последний заказ» как единственный способ найти свои данные: другой пользователь или параллельный тест может создать запись позже.

    5. Найти невозможный status

    sql
    1SELECT id, status
    2FROM orders
    3WHERE status NOT IN ('PENDING', 'PAID', 'PREPARING', 'DONE', 'CANCELLED');

    Даже если БД или приложение ограничивают enum на уровне кода, такой запрос полезен при миграциях и импортах данных.

    6. Найти подозрительные lifecycle-состояния

    sql
    1SELECT id, status, paid_at, completed_at
    2FROM orders
    3WHERE (status = 'PENDING' AND paid_at IS NOT NULL)
    4   OR (status = 'DONE' AND completed_at IS NULL);

    Это пример проверки бизнес-инвариантов. Перед тем как заводить баг, обязательно сверяй найденное состояние с реальными требованиями и асинхронностью процесса.

    7. JOIN заказа и рецепта

    sql
    1SELECT
    2    o.id AS order_id,
    3    o.status,
    4    o.qty,
    5    o.total_price,
    6    r.id AS recipe_id,
    7    r.name AS recipe_name,
    8    r.size AS recipe_size,
    9    r.price AS current_recipe_price
    10FROM orders o
    11JOIN recipes r ON r.id = o.recipe_id
    12WHERE o.id = 7812;

    Так можно сверить вложенный OrderResponse.recipe с фактическим recipes.id/name/size. Не сравнивай total_price с текущим recipes.price спустя долгое время без знания бизнес-правила: цена рецепта могла измениться после оформления заказа.

    8. Найти рецепты по части имени и размеру

    sql
    1SELECT id, name, size, price, prep_seconds
    2FROM recipes
    3WHERE name ILIKE '%сыр%'
    4  AND size = 'LARGE'
    5ORDER BY name;

    Это удобно сравнивать с GET /api/v1/recipes?query=...&recipeSize=LARGE.

    9. Найти дубли имён рецептов

    sql
    1SELECT name, COUNT(*)
    2FROM recipes
    3GROUP BY name
    4HAVING COUNT(*) > 1;

    name объявлен UNIQUE NOT NULL, поэтому результат должен быть пустым. Хорошая проверка после миграций/импортов; PATCH /api/v1/recipes/{id} также документирует 409 при занятом имени.

    10. Проверить состав рецепта через many-to-many

    sql
    1SELECT
    2    r.id AS recipe_id,
    3    r.name AS recipe_name,
    4    i.id AS ingredient_id,
    5    i.name AS ingredient_name,
    6    ri.qty_needed,
    7    i.unit,
    8    i.on_hand
    9FROM recipe_ingredients ri
    10JOIN recipes r ON r.id = ri.recipe_id
    11JOIN ingredients i ON i.id = ri.ingredient_id
    12WHERE r.id = 42
    13ORDER BY i.name;

    Сравнивай с GET /api/v1/recipes/{id}/ingredients: API возвращает recipeName и список ingredientId/name/qtyNeeded/unit/onHand.

    Many-to-many в реальной работе QA: один запрос позволяет проверить состав рецепта и текущие остатки ингредиентов.

    11. Найти LOW STOCK так же, как это делает бизнес-логика

    sql
    1SELECT id, name, unit, on_hand, min_level, supplier_channel
    2FROM ingredients
    3WHERE on_hand < min_level
    4ORDER BY (min_level - on_hand) DESC;

    Это естественная DB-проверка для GET /api/v1/ingredients?lowStock=true.

    12. Проверить supplier channel

    sql
    1SELECT id, name, supplier_code, supplier_channel, supplier_lead_time_days
    2FROM ingredients
    3WHERE supplier_channel IN ('SOAP', 'REST', 'GRPC')
    4ORDER BY supplier_channel, name;

    Поле supplier_channel — хорошая точка для интеграционных сценариев: разные ингредиенты могут идти через SOAP, REST или gRPC.

    13. История платежей по конкретному заказу

    sql
    1SELECT id, order_id, idempotency_key, amount, method, status, txn_id, failure_reason, created_at
    2FROM payments
    3WHERE order_id = 7812
    4ORDER BY created_at;

    Сверяй с GET /api/v1/orders/{id}/payments. Особенно полезно для повторных запросов оплаты.

    14. Проверить идемпотентность платежа по Idempotency-Key

    sql
    1SELECT idempotency_key, COUNT(*) AS payment_rows
    2FROM payments
    3WHERE idempotency_key = '550e8400-e29b-41d4-a716-446655440000'
    4GROUP BY idempotency_key;

    После двух POST /api/v1/orders/{id}/pay с одним и тем же Idempotency-Key ожидаем одну логическую запись платежа. В БД ключ дополнительно UNIQUE NOT NULL — это отличный пример того, как API-требование поддерживается persistence-ограничением.

    API обещает безопасный retry через Idempotency-Key, а БД хранит этот ключ в payments как UNIQUE NOT NULL. Здесь Manual QA уже проверяет не только HTTP-ответ, но и persistence.

    15. Сверить order и payment между собой

    sql
    1SELECT
    2    o.id AS order_id,
    3    o.status AS order_status,
    4    o.total_price,
    5    o.payment_method AS order_payment_method,
    6    p.amount AS payment_amount,
    7    p.method AS payment_method,
    8    p.status AS payment_status,
    9    p.txn_id
    10FROM orders o
    11JOIN payments p ON p.order_id = o.id
    12WHERE o.id = 7812
    13ORDER BY p.created_at DESC;

    Проверяем согласованность amount/total_price и method/payment_method. Учитывай, что у заказа может быть история нескольких попыток платежа — не делай вывод по случайной строке без created_at/status.

    16. Последние FAILED платежи

    sql
    1SELECT id, order_id, amount, method, txn_id, failure_reason, created_at
    2FROM payments
    3WHERE status = 'FAILED'
    4ORDER BY created_at DESC
    5LIMIT 20;

    failure_reason должен помогать диагностировать отказ. Если причина теряется, это отдельный вопрос к observability и контракту.

    17. Найти заказы без платежей

    sql
    1SELECT o.id, o.status, o.total_price, o.placed_at
    2FROM orders o
    3LEFT JOIN payments p ON p.order_id = o.id
    4WHERE p.id IS NULL
    5ORDER BY o.placed_at DESC;

    Для нового PENDING-заказа это может быть нормальным до /pay. Главное правило SQL-тестирования: отсутствие строки не равно багу без бизнес-контекста.

    18. Проверить инвойсы пополнения ингредиента

    sql
    1SELECT
    2    ri.id,
    3    i.name AS ingredient_name,
    4    ri.invoice_number,
    5    ri.total_cost,
    6    ri.status,
    7    ri.eta,
    8    ri.warehouse_code
    9FROM restock_invoices ri
    10JOIN ingredients i ON i.id = ri.ingredient_id
    11WHERE ri.ingredient_id = 7
    12ORDER BY ri.eta DESC;

    Это полезно после POST /api/v1/ingredients/{id}/restock, особенно для SOAP-поставщика и сценариев ACCEPTED / WAITLIST / REJECTED.

    19. Посмотреть последние Kafka-события в event_log

    sql
    1SELECT id, type, jsonb_pretty(payload) AS payload, ts
    2FROM event_log
    3ORDER BY ts DESC
    4LIMIT 20;

    event_log — read-модель страницы EVENTS, которую наполняет Kafka-consumer. OpenAPI также отдаёт эти данные через GET /api/v1/events.

    20. Отфильтровать конкретный тип события

    sql
    1SELECT id, type, payload, ts
    2FROM event_log
    3WHERE type = 'ORDER_PLACED'
    4ORDER BY ts DESC
    5LIMIT 20;

    В схеме type хранит значения вроде ORDER_PLACED. Точную структуру payload не надо угадывать: сначала посмотри реальный JSON, затем уже пиши проверки JSONB по конкретным ключам.

    Сильный тест: заказ → платёж → БД → Kafka

    Теперь соберём всё в один сценарий, который отлично показывает разницу между «я умею SELECT» и «я умею тестировать backend».

    text
    11. POST /api/v1/orders
    2   → HTTP 200
    3   → сохранить orderId
    4   → status = PENDING
    5
    62. SELECT FROM orders WHERE id = orderId
    7   → recipe_id / qty / total_price / status совпадают с API
    8
    93. POST /api/v1/orders/{id}/pay
    10   Header: Idempotency-Key = <UUID>
    11
    124. повторить тот же POST с тем же Idempotency-Key
    13
    145. SELECT FROM payments WHERE idempotency_key = <UUID>
    15   → не появился второй платёж для того же ключа
    16   → amount/method/status согласованы
    17
    186. SELECT FROM orders WHERE id = orderId
    19   → проверить переход состояния и paid_at по фактическому результату оплаты
    20
    217. SELECT FROM event_log ORDER BY ts DESC
    22   → найти связанные доменные события и проверить их payload
    Один бизнес-сценарий связывает HTTP-контракт и persistence: заказ, платёж и идемпотентность проверяются по общему order_id.

    Ещё 5 вещей в схеме, которые должен заметить QA

    • ▸recipes.name — UNIQUE NOT NULL: проверяем конфликт имён и поведение PATCH.
    • ▸payments.idempotency_key — UNIQUE NOT NULL: persistence защищает от дубля при retry.
    • ▸ingredients.on_hand и min_level задают LOW STOCK: это можно проверить и через API, и через SQL.
    • ▸orders хранит paid_at, completed_at и bread_batch_id: lifecycle можно проверять не только по status.
    • ▸event_log.payload — JSONB: сначала изучаем реальный payload, потом пишем JSONB-проверки, а не предполагаем его структуру.

    А что с audit-полями?

    В recipes, ingredients и orders есть created_at/updated_at/created_by/updated_by. Это полезно при тестировании PATCH и административных операций: можно проверить не только бизнес-поле, но и корректность аудита пользователя из JWT.

    sql
    1SELECT id, name, created_at, updated_at, created_by, updated_by
    2FROM recipes
    3WHERE id = 42;

    Ошибки Manual QA при работе с SQL

    Ошибка 1. Считать любую несостыковку багом

    Асинхронность, очереди и background processing создают промежуточные состояния. Например, payment SUCCEEDED и переход заказа в PREPARING могут происходить не одной атомарной строкой кода. Учитывай SLA/требования и повторяй проверку осмысленно.

    Ошибка 2. Искать свою запись через ORDER BY ... LIMIT 1

    На shared environment используй id из API response, UUID/idempotency key или другой уникальный correlation key.

    Ошибка 3. Забывать про snapshot-данные

    orders.total_price — сумма конкретного заказа, а recipes.price — текущая цена рецепта. Если рецепт потом подорожал, их различие не обязано быть ошибкой.

    Ошибка 4. Делать JOIN без понимания cardinality

    orders → payments — one-to-many. Один заказ может иметь историю попыток оплаты, поэтому JOIN способен вернуть несколько строк. Всегда понимай, почему строк стало больше.

    Ошибка 5. Трогать password_hash или секреты без необходимости

    users.password_hash нужен системе, но почти никогда не нужен в обычной QA-выборке. Не выгружай чувствительные поля просто потому, что у тебя есть доступ.

    Как эта же проверка выглядит в Java Automation

    Manual QA сначала выполняет цепочку Postman → response → DBeaver → SELECT. Автотест может выполнить ту же проверку в одном запуске.

    java
    1long orderId = given()
    2    .contentType(ContentType.JSON)
    3    .body(orderRequest)
    4.when()
    5    .post("/api/v1/orders")
    6.then()
    7    .statusCode(200)
    8    .body("status", equalTo("PENDING"))
    9    .extract()
    10    .jsonPath()
    11    .getLong("id");
    12
    13OrderRow order = orderRepository.findById(orderId);
    14
    15assertThat(order.status()).isEqualTo("PENDING");
    16assertThat(order.qty()).isEqualTo(orderRequest.qty());
    17assertThat(order.recipeId()).isEqualTo(orderRequest.recipeId());

    Здесь OrderRow — уже не абстрактная сущность: он маппится на реальные orders.id / recipe_id / qty / total_price / status / placed_at и другие поля. Repository можно реализовать через JDBC или другой data-access слой.

    А сценарий идемпотентности можно автоматизировать ещё сильнее: дважды вызвать /pay с одним Idempotency-Key, затем SQL-запросом проверить количество строк payments по этому ключу.

    Именно поэтому переход из Manual QA в Java Automation логичен: ты не учишь новую профессию с нуля — ты переносишь уже знакомые проверки HTTP, API и БД в повторяемый код.

    В Java QA Automation ThreadQA эти части соединяются вокруг ShawarmaShop: REST Assured для API, PostgreSQL/JDBC для persistence, Kafka для событий и Selenide для UI.

    Практика: сделай эту проверку самостоятельно

    • ▸1. Создай заказ через Swagger или Postman и сохрани orderId.
    • ▸2. Найди запись в orders и сверь recipe_id, qty, total_price, status.
    • ▸3. Через JOIN подтяни recipes и сравни вложенный recipe из API.
    • ▸4. Оплати заказ с уникальным Idempotency-Key.
    • ▸5. Повтори оплату с тем же ключом и проверь payments.
    • ▸6. Посмотри order/payment lifecycle fields.
    • ▸7. Найди связанные события в event_log и изучи реальный JSONB payload.

    Если ты уверенно можешь пройти UI/API → DB → payment → event, то SQL уже стал рабочим инструментом тестировщика, а не набором SELECT для собеседования.

    FAQ

    Какой SQL нужен Manual QA?

    Для ежедневной работы начни с SELECT, WHERE, IN, BETWEEN, NULL, ORDER BY, LIMIT, COUNT/SUM/AVG, GROUP BY/HAVING, INNER JOIN и LEFT JOIN. После этого добавляй CTE/subquery и JSONB под конкретные задачи проекта.

    Нужно ли Manual QA знать структуру БД?

    Не всю наизусть. Важно уметь быстро понять PK/FK, cardinality и поля бизнес-сущности, которую проверяешь. В ShawarmaShop для заказа это в первую очередь orders → recipes и orders → payments.

    Что проверять в БД после API-запроса?

    Не только факт существования строки. Проверяй бизнес-поля, lifecycle timestamps, связанные сущности, уникальность, отсутствие нежелательных дублей и согласованность данных между слоями.

    Зачем тестировщику JSONB?

    В ShawarmaShop event_log.payload хранится как jsonb. Для начала достаточно уметь посмотреть payload и понять структуру; затем можно фильтровать по конкретным JSON-ключам, когда известен реальный формат события.

    Что важнее для перехода в Automation: SQL или Java?

    SQL быстрее усиливает текущую manual-работу. Если цель — Automation, Java стоит учить параллельно: REST Assured создаёт данные через API, JDBC/PostgreSQL проверяет persistence, а дальше всё объединяется в один автотест.


    Что изучить дальше

    Свяжи API и БД: Postman для тестировщика
    Научись читать контракт: Swagger/OpenAPI для QA
    Перенеси эти проверки в Java QA Automation
    #sql для тестировщика#sql qa#sql запросы для тестировщика#sql для manual qa#postgresql qa#проверка базы данных qa#api database testing#jdbc java qa
    MANUAL QA → AUTOMATION

    Уже умеешь проверять это руками? Следующий шаг — автоматизировать

    На Java QA Automation знакомые ручные проверки превращаются в код: Postman и HTTP — в REST Assured, UI и локаторы — в Selenide, SQL — в PostgreSQL/JDBC. Ты не начинаешь профессию заново, а переносишь уже знакомую логику тестирования в автотесты.

    Java 21 · REST Assured · Selenide · Kafka
    Письменная проверка практических заданий
    ShawarmaShop · Docker · CI/CD · карьерный блок
    Посмотреть программу Java QAJava QA с нуля: полный roadmapСтоимость курса: 65 000 ₽

    Читайте также

    Обучение
    22 мин

    Postman для тестировщика: как тестировать REST API с нуля

    Практический гайд по Postman для Manual QA: запросы, headers, body, авторизация, негативные проверки и переход к REST Assured на Java.

    Обучение
    20 мин

    Chrome DevTools для тестировщика: Network, Console, Application и поиск багов

    Как Manual QA использовать Chrome DevTools: Network, Console, cookies, storage, headers, API-запросы и практический поиск причины бага.

    Обучение
    22 мин

    HTTP для тестировщика: методы, коды, headers, cookies и реальные проверки

    HTTP без лишней теории для Manual QA: GET/POST/PUT/PATCH/DELETE, status codes, headers, cookies, idempotency и примеры API-проверок.

    Все статьи блога

    Содержание

    Реальная схема ShawarmaShop: что с чем связаноAPI → DB: как связать проверку20 SQL-запросов, которые реально пригодятся QAСильный тест: заказ → платёж → БД → KafkaЕщё 5 вещей в схеме, которые должен заметить QAОшибки Manual QA при работе с SQLКак эта же проверка выглядит в Java AutomationПрактика: сделай эту проверку самостоятельноFAQЧто изучить дальше

    Автор

    Олег Пендрак
    Олег Пендрак
    Tech Lead QA

    Опыт в Ozon и VK. YouTube-канал 10к+ подписчиков.

    MANUAL QA → AUTOMATION

    Java QA Automation

    Реальный проект, письменный code review и полный стек от Java Core до CI/CD.

    65 000 ₽
    Посмотреть курс
    THREADQAПлатформа QA Automation

    О платформе

    Обучаем автоматизации тестирования на Java, Python и iOS. Курсы, мок-интервью, буткемп с менторством до оффера.

    Онлайн 24/7

    Курсы

    • Java QA AutomationNEW
    • Python QA Automation
    • iOS QA Automation
    • Про ThreadQA

    Услуги

    • QA Буткемп
    • Мок-собеседования
    • Записи собеседований

    Инструменты

    • Roadmap QA
    • Тренажёр XPath
    • XPath Diner

    Контакты

    • Email
      info@threadqa.ru
    • Telegram
      @penolegrus
    Публичная офертаПолитика конфиденциальностиУсловия использования
    © 2026·ThreadQA LMS·Все права защищены