Глеб Зайцев
Маркетплейс в SQL. 18 задач для продавца и аналитика

Перед первой задачей

Маркетплейс любит маленькие ошибки

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

Просмотры без корзин, корзины без оплат и продажи без чистой прибыли — разные проблемы. Их нельзя чинить одним и тем же действием.

Эта книга показывает, как разложить витрину на проверяемые SQL-метрики и быстрее увидеть реальный блокер роста.

Как читать задачи

Каждая задача использует короткую синтетическую выгрузку: офферы, остатки, просмотры, заказы, расходы, конкуренты или доставка.

Перед решением полезно назвать управленческое действие: пополнить остаток, переписать карточку, снизить риск буста, поднять цену или снять товар.

Это учебные примеры, а не инструкция по конкретному кабинету. Задача — научиться считать логику, которую потом можно перенести в реальную выгрузку.

Маршрут по задачам

1. Конверсия карточки в корзину и оплату

2. Найти реально доступные к продаже товары

3. Чистая прибыль по оплаченным заказам

4. Безопасный процент буста от продажи

5. Сравнить цену с минимальной ценой конкурента

6. Потерянный спрос из-за нулевого остатка

7. Карточки с просмотрами, но без корзин

8. Возвраты по SKU

9. Остатки по складам

10. Запросы покупателей без оплат

11. Позиция в выдаче и CTR

12. Опоздания доставки

13. Комиссия и прибыль по категориям

14. Отобрать товары с безопасной маржой

15. Трёхдневный тренд просмотров

16. Карточки без базового контента

17. Разделить органику и буст в продажах

18. Финальный дневной отчёт продавца

Практические задачи

Задача 1. Конверсия карточки в корзину и оплату

Рабочий вопрос

Нужно сравнить просмотры, добавления в корзину и оплаченные заказы по SKU, чтобы не смешивать проблемы трафика и карточки.

Данные

CREATE TABLE offer_stats(sku TEXT, views INTEGER, carts INTEGER, paid_orders INTEGER);

INSERT INTO offer_stats VALUES

('A',100,5,2),

('B',60,1,0),

('C',40,0,0);

Запрос

SELECT sku,

views,

carts,

paid_orders,

ROUND(carts * 100.0 / views, 2) AS cart_conversion_pct,

CASE WHEN carts = 0 THEN 0

ELSE ROUND(paid_orders * 100.0 / carts, 2)

END AS order_from_cart_pct

FROM offer_stats

ORDER BY views DESC;

Ожидаемый результат

[["A", 100, 5, 2, 5.0, 40.0], ["B", 60, 1, 0, 1.67, 0.0], ["C", 40, 0, 0, 0.0, 0]]

Почему работает

cart_conversion_pct показывает реакцию на карточку.

order_from_cart_pct показывает, сколько корзин дошло до оплаты.

Для нулевой корзины нужен отдельный CASE, чтобы не делить на ноль.

Где легко ошибиться

Если смотреть только просмотры, SKU C может казаться живым, хотя у него нет ни одной корзины.

Самостоятельная проверка

Какой SKU первым требует проверки названия, цены или обложки карточки?

Ответ: SKU C: просмотры есть, но корзин нет, значит карточка не убеждает покупателя.

Задача 2. Найти реально доступные к продаже товары

Рабочий вопрос

Нужно отделить опубликованные и покупаемые товары от карточек, которые формально есть, но заблокированы остатком или статусом.

Данные

CREATE TABLE offers(sku TEXT, status TEXT, stock INTEGER, freeze INTEGER, quarantine INTEGER);

INSERT INTO offers VALUES

('A','PUBLISHED',25,0,0),

('B','PUBLISHED',0,0,0),

('C','PUBLISHED',10,1,0),

('D','PUBLISHED',8,0,1),

('E','DRAFT',30,0,0);

Запрос

SELECT sku, stock

FROM offers

WHERE status = 'PUBLISHED'

AND stock > 0

AND freeze = 0

AND quarantine = 0

ORDER BY sku;

Ожидаемый результат

[["A", 25]]

Почему работает

A опубликован и имеет положительный остаток.

B опубликован, но остаток равен нулю.

C и D исключены из-за freeze и quarantine.

Где легко ошибиться

Считать все PUBLISHED карточки активным ассортиментом опасно: часть витрины может быть недоступна к покупке.

Самостоятельная проверка

Почему DRAFT с остатком 30 не считается доступным товаром?

Ответ: Покупатель не видит черновик на витрине, поэтому остаток у DRAFT не создаёт продажу.

Задача 3. Чистая прибыль по оплаченным заказам

Рабочий вопрос

Нужно посчитать прибыль после комиссии, эквайринга, налога и себестоимости только по фактически оплаченным заказам.

Данные

CREATE TABLE orders(id INTEGER, status TEXT, gross INTEGER, market_fee INTEGER, acquiring INTEGER, tax INTEGER, cost INTEGER);

INSERT INTO orders VALUES

(1,'paid',1000,140,20,60,200),

(2,'paid',500,70,10,30,100),

(3,'new',900,126,18,54,180);

Запрос

SELECT id,

gross,

gross - market_fee - acquiring - tax - cost AS net_profit

FROM orders

WHERE status = 'paid'

ORDER BY id;

Ожидаемый результат

[[1, 1000, 580], [2, 500, 290]]

Почему работает

В расчёт попадают только paid-заказы.

Заказ 1 даёт 1000 - 140 - 20 - 60 - 200.

Новый заказ 3 не должен попадать в прибыль.

Где легко ошибиться