Как подходить к решению SQL задачи?
Что проверяют
Не столько синтаксис, сколько порядок мысли: поняли ли вы бизнес-вопрос, знаете ли, что означает одна строка результата, и проверяете ли цифры, прежде чем их отдать. Аналитику почти всегда дают задачу на две-три таблицы с подвохом в JOIN или NULL, а потом спрашивают, как вы убедились, что ответ верный.
Как отвечать
Проговорите шаги, которые вы выполняете на каждой задаче:
- зерно результата: одна строка это что? Клиент, клиент за месяц, заказ? Пока это не ясно, писать запрос рано. Сразу уточните неоднозначное: «активный пользователь» по какому событию, какие статусы заказа считать, в каком часовом поясе даты;
- разведка данных: посмотрите на ключи таблиц, есть ли дубли, сколько NULL в важных полях. Пара запросов с
COUNT(*)иCOUNT(DISTINCT id)экономит много времени; - сборка по частям: сначала фильтр и соединение, проверка числа строк, затем агрегация. Промежуточные шаги удобно выносить в CTE через
WITH, их можно запускать по отдельности; - логический порядок выполнения:
FROMиJOIN,WHERE,GROUP BY,HAVING,SELECT,ORDER BY,LIMIT. Отсюда понятно, почему в большинстве СУБД алиас изSELECTне виден вWHERE, а условие на агрегат идёт вHAVING; - оконные функции:
ROW_NUMBER()для «последнего заказа клиента»,LAG()для разницы с прошлым периодом,SUM() OVERдля накопительного итога; - сверка: сравните итог с известной цифрой из отчёта или посчитайте тот же показатель другим способом.
Где спотыкаются
Соединяют таблицы «один ко многим» и затем суммируют поле из верхней таблицы. Строки размножились, выручка выросла вдвое, а запрос выглядит правильным.
Ставят условие на правую таблицу LEFT JOIN в WHERE. NULL не проходит фильтр, и соединение фактически становится внутренним. Если нужно сохранить все строки левой таблицы, условие переносят в ON.
Путают COUNT(*) и COUNT(column): второй не считает NULL. Сюда же NOT IN по подзапросу, в котором есть NULL: он вернёт пустой результат, безопаснее NOT EXISTS.