⌛️ Большую часть времени аналитики пишут скрипты в определённой СУБД: достают оттуда данные для моделей, отчётности, выгрузок, продуктовых исследований и прочих задач. Предположим, ты начал строить большую витрину, которая должна покрывать бизнес-потребности.
Всё идёт нормально, но вдруг:
1. Нет записей, хотя должны быть / записей стало меньше
2. Данные задвоились
3. Результаты не сходятся с дашбордом / другой внутренней системой (например, в 1С / сервисе заказов и тд)
____
Этот пост - про быструю и понятную отладку SQL-запросов, особенно если он уже раздулся на тысячи строк.
1️⃣ Начало с верхнеуровневой структуры
Если в коде есть подзапросы, лучше переписать их на CTE / временные таблицы. Так код легче читать и отлаживать по шагам.
Простой подзапрос:
select ...
from (
select ...
from orders
where ...
) t
join ...
CTE:
with filtered_orders AS (
select ...
from orders
where ...
)
select ...
from filtered_orders
join ...
Стало чуточку проще читать + можно проверить, что в
filtered_orders, следующий шаг про это2️⃣ Проверка CTE или временных таблиц
Здесь мы проверяем количество строк / уникальных сущностей по типу
order_id / user_id, проверяем на пустые значения
select count(*) as total_rows,
count(distinct user_id) as unique_users
from filtered_orders;
3️⃣ Спускаемся глубже, смотрим с какого момента началась проблема (идем внутрь запроса)
Что нас ждет внутри? Джойны / оконные функции / группировки.
Хорошая практика - это посмотреть, задублировались ли ключи, по которым будет в дальнейшем JOIN
select o.order_id, count(*) as cnt
from orders o
join transactions t on o.order_id = t.order_id
group by o.order_id
having count(*) > 1;
Если дублируется, то надо ответить на вопрос: ожидаемое это поведение или нет? Если проблема, то следующий шаг.
4️⃣ Контроль за дублями
Базовая проблема: в одной таблице ключ уникален, в другом нет (можно, например, предагрегировать, используя
row_number() / distinct / group by
with transaction_agg as (
select order_id, sum(amount) as total_amount
from transactions
group by order_id
)
select o.order_id, t.total_amount
from orders o
left join transaction_agg as t ON o.order_id = t.order_id;
А если так нельзя схлопнуть, можно атрибуцировать за какой-то промежуток времени и связывать по дню, например
5️⃣Хорошая и простая практика: посмотреть глазами
Берем значение ключа, по которому связываем и смотрим, как дублируется, из-за чего. Возможно, на транзакции приходится несколько записей с типом оплаты (и это надо предусмотреть)
select *
from orders o
join transactions t on o.order_id = t.order_id
where o.order_id = 'abc123';
6️⃣Последнее
Действительно я понимаю данные, которые используются при сборе витрины?
Бывают разные сущности, но хочется понимать как мы закрываем бизнес-задачу, используя именно ЭТИ данные (тут про смысл аналитического мышления / бизнес-смысла и смысла данных
Понравился формат поста? Ставьте 🔥, пишите комментарии, какие пункты еще стоит добавить