К содержимому

ИИ-аналитика · PRO

SQL с помощью ИИ: как получать рабочие запросы и не пропустить ошибку

· 13 мин чтения · Редакция ultrathink

Писать SQL с помощью ChatGPT или другой языковой модели можно и нужно, но запрос, который выполнился без ошибки, ещё не значит, что он посчитал правильно. Модель хорошо пишет синтаксис и плохо знает ваши данные. Поэтому результат зависит от двух вещей: насколько точно вы описали задачу и насколько системно проверяете то, что получили.

Ниже — рабочий процесс, которым пользуются аналитики, когда пишут запросы вместе с моделью: как дать контекст, как собирать запрос по шагам, какие ошибки встречаются чаще всего и как поймать каждую из них до того, как цифра попадёт в отчёт. Все примеры построены на условной схеме и служат только иллюстрацией.

Как поставить задачу модели

Модель не видит вашу базу. Всё, чего нет в вашем сообщении, она додумает — и додумает правдоподобно. Названия таблиц, смысл статусов, валюта суммы, часовой пояс дат — если вы это не указали, модель выберет самый вероятный вариант из общего опыта, а не тот, что принят у вас.

Контекст: диалект и схема

  1. 01Укажите диалект: PostgreSQL, MySQL, ClickHouse, MS SQL и другие СУБД по-разному работают с датами, строками и оконными функциями.
  2. 02Передайте схему: названия таблиц, поля, типы, первичные и внешние ключи.
  3. 03Опишите связи словами: у одного заказа может быть несколько платежей, у клиента — несколько заказов.
  4. 04Объясните смысл неочевидных полей: статусы, флаги, коды, валюты, часовой пояс.
  5. 05Перечислите фильтры, которые применяются всегда: тестовые записи, внутренние клиенты, отменённые операции.

Определение метрики словами

До того как просить запрос, запишите метрику так, как вы объяснили бы её новому коллеге. Не «выручка за месяц», а «сумма оплаченных заказов без учёта возвратов, по дате оплаты, в местном времени, без тестовых клиентов». Такое определение полезно не только модели: оно заставляет вас самого ответить на вопросы, которые иначе всплыли бы уже после отправки отчёта.

Гранулярность результата

Опишите ожидаемый результат: какие колонки и что означает одна строка. Фраза «одна строка на клиента в месяц» сразу задаёт гранулярность и даёт простой тест: если в результате один клиент встретился в одном месяце дважды, где-то ошибка. Без такой фразы модель может вернуть вполне разумную на вид таблицу с другим уровнем детализации.

Пошаговый процесс: от вопроса до проверенной цифры

Надёжный результат получается не из одного удачного ответа модели, а из процесса, где каждый шаг проверяется. Порядок ниже кажется длинным, но большинство шагов занимает минуты, а сэкономить он может дни разбирательств, почему отчёт не сходится.

  1. 01Сформулируйте вопрос бизнеса и метрику словами, согласуйте определение с тем, кто будет пользоваться цифрой.
  2. 02Соберите контекст: диалект, схема, связи, смысл полей, обязательные фильтры.
  3. 03Попросите модель сначала описать план запроса по шагам, без кода — так видно, правильно ли она поняла задачу.
  4. 04Попросите код, разбитый на шаги через CTE, с комментарием к каждому шагу.
  5. 05Выполните шаги по одному, проверяя количество строк, уникальность ключей и суммы.
  6. 06Сверьте итог с независимым источником или ручным подсчётом на маленьком отрезке.
  7. 07Проверьте крайние случаи: пустые периоды, объекты без связанных записей, NULL.
  8. 08Сохраните финальный запрос вместе с определением метрики и выполненными проверками.

Шаг с планом без кода особенно полезен. Если модель пишет «сначала соединяем заказы с платежами, потом суммируем сумму заказа», вы ещё до запуска видите проблему: сумма заказа размножится на число платежей. Поправить план проще, чем найти ошибку в готовом запросе.

Джойны и размножение строк

Самая частая и самая опасная ошибка — джойн, который тихо размножает строки. Запрос выполняется, цифра выглядит разумно, и ошибка уходит в отчёт. Модель допускает её регулярно, потому что не знает, какое отношение между таблицами у вас на самом деле.

Как это выглядит

Возьмём условную схему из примера выше. Допустим, заказ оплачен двумя частями. Если соединить orders с payments и посчитать сумму поля заказа, этот заказ попадёт в сумму дважды. Количество заказов через COUNT(*) тоже завысится. При этом запрос формально верен и ошибок не выдаёт.

Как избежать

  • Перед каждым джойном определите отношение: один к одному, один ко многим, многие ко многим.
  • Если отношение «ко многим», сначала агрегируйте вторую таблицу до уровня первой, затем присоединяйте.
  • Считайте метрику на той таблице, где она живёт: сумму платежей — по платежам, число заказов — по заказам.
  • Попросите модель явно указать гранулярность каждого промежуточного шага.
  • Будьте осторожны с LEFT JOIN и последующим фильтром по правой таблице в WHERE: он превращает соединение во внутреннее.

Как поймать

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

Дубли, NULL и агрегации

Дубли бывают не только от джойнов. В исходных таблицах встречаются повторные загрузки, тестовые записи, отменённые и восстановленные операции, несколько версий одной записи. Модель не знает об этом, пока вы не скажете, поэтому правила исключения нужно сформулировать явно и проверить, что они действительно применились.

Ловушки NULL

NULL ведёт себя не так, как подсказывает интуиция. Это не ноль и не пустая строка, а отсутствие значения, и любое сравнение с ним даёт неопределённый результат. Модели обычно знают это правило, но применяют его не всегда — особенно когда бизнес-смысл пустого значения не объяснён.

  • Фильтр status <> 'cancelled' выбросит строки, где статус пустой, — хотя их, возможно, нужно было оставить.
  • COUNT(*) и COUNT(поле) дают разные числа, если в поле есть NULL.
  • AVG считает среднее только по заполненным значениям; среднее с заменой NULL на ноль — другая метрика.
  • Сумма столбца, где все значения пустые, вернёт NULL, а не ноль, и это может сломать дальнейшие расчёты.
  • Условие NOT IN со списком, в котором есть NULL, может не вернуть ни одной строки.
  • Деление на ноль или на NULL в долях даёт ошибку или пустой результат в зависимости от СУБД.

COUNT DISTINCT как маскировка

Если цифры завышены, модель часто предлагает добавить DISTINCT. Иногда это правильно, но нередко DISTINCT просто прячет размножение строк в одном месте, оставляя его в другом: количество заказов становится верным, а сумма — по-прежнему завышенной. Прежде чем соглашаться на DISTINCT, выясните, откуда взялись повторы.

Даты, периоды и часовые пояса

Границы периода — источник тихих расхождений, которые трудно заметить глазами. Отчёт за месяц может отличаться от учётной системы на небольшую величину, и никто не поймёт почему, пока не разберёт по дням.

  • Условие «меньше или равно последнему дню месяца» для поля с датой и временем потеряет все события этого дня после полуночи.
  • Надёжнее полуоткрытый интервал: от первого дня месяца включительно до первого дня следующего месяца не включительно.
  • Если даты хранятся в UTC, а отчёт нужен по местному времени, граница дня сдвигается, и часть событий переходит в соседний день или месяц.
  • Функция усечения даты до месяца или недели работает в часовом поясе сессии, если не указать его явно.
  • Неделя в разных СУБД и настройках может начинаться с разного дня.
  • «Прошлый месяц» относительно текущей даты зависит от того, когда запрос запущен, — для регулярных отчётов лучше передавать даты явно.

Ещё одна частая неоднозначность — какую дату брать. У заказа есть дата создания, оплаты, отгрузки и возврата. Выручка по дате создания и по дате оплаты — разные цифры, и обе могут быть правильными. Укажите модели нужную дату явно и запишите это в определение метрики.

Сложные запросы: собирайте по шагам

Чем длиннее запрос, тем выше шанс, что ошибка спрячется в середине. Вместо одного огромного запроса попросите модель разбить его на шаги через CTE — общие табличные выражения — и проверяйте каждый шаг отдельно: сколько в нём строк, какая гранулярность, совпадают ли суммы с предыдущим шагом.

Оконные функции

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

  • Проверяйте PARTITION BY: соответствует ли он сущности, по которой нужен расчёт.
  • Проверяйте ORDER BY: однозначен ли порядок, нет ли одинаковых значений.
  • Проверяйте рамку окна для накопительных и скользящих расчётов.
  • Для «первого» или «последнего» события уточните, что делать при равенстве дат.

Рабочий приём

Просите комментарий к каждому шагу: что он делает и что означает одна строка. Запускайте шаги по одному на небольшом отрезке данных, например за один день или для одного клиента, где результат можно проверить глазами. Сохраняйте финальную версию вместе с промежуточными проверками: проверенные шаги можно переиспользовать, а модель получает их как готовый контекст для следующих задач.

Сверка с источником и контрольные суммы

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

  1. 01Посчитайте общий итог без группировки и сравните с суммой по группам.
  2. 02Возьмите один конкретный объект — клиента, заказ, день — и проверьте его вручную по сырым данным.
  3. 03Сравните с известным числом из другой системы.
  4. 04Проверьте крайние случаи: пустой период, клиент без заказов, заказ без платежа, платёж без заказа.
  5. 05Сравните результат с прошлой версией отчёта, если она есть, и объясните каждое расхождение.
  6. 06Попросите модель объяснить запрос по шагам и сравните объяснение со своей задачей.

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

Типичные ошибки и как поймать каждую

Большинство ошибок в запросах, написанных с помощью модели, повторяются. Удобно держать перед глазами короткий список: что может пойти не так и какой проверкой это ловится.

  • Выдуманные таблицы или поля — передавайте схему целиком; если запрос упал с ошибкой «нет такого поля», не позволяйте модели угадывать замену.
  • Размножение строк в джойне — сравнение COUNT(*) и COUNT(DISTINCT ключ) после каждого соединения.
  • Потеря строк во внутреннем джойне — сравнение числа строк основной таблицы до и после.
  • Неверная обработка NULL — отдельный подсчёт строк с пустыми значениями в ключевых полях.
  • Сдвиг границ периода — сверка дневной разбивки с источником на стыке двух дней или месяцев.
  • Подмена определения метрики — сравнение объяснения модели с записанным определением.
  • Неверная дата — проверка, по какому полю идёт фильтр и группировка.
  • Забытые фильтры по умолчанию — проверка, что тестовые и внутренние записи не попали в результат.
  • Неоднозначная сортировка в окне — повторный запуск и сравнение результатов.
  • Арифметика в тексте ответа — итоговые числа берите только из результата запроса, а не из пояснения модели.

Когда просить модель, а когда писать самому

Модель не обязательно использовать для каждого запроса. Решение зависит от того, насколько легко проверить результат и насколько дорога ошибка.

  • Простой запрос к знакомым таблицам — модель экономит время на наборе, проверка занимает минуты.
  • Незнакомый синтаксис или функция другой СУБД — модель полезна как справочник, но проверяйте поведение на тестовых данных.
  • Сложная логика с несколькими джойнами и окнами — просите план и шаги, а не готовый запрос целиком.
  • Финансовые и регуляторные цифры — модель может написать черновик, но проверка обязательна, а запрос должен пройти ревью коллеги.
  • Разовый исследовательский вопрос — допустима быстрая версия, если вывод помечен как предварительный.

Ревью коллеги остаётся полезным и тогда, когда запрос написан с помощью модели. Покажите ему не только код, но и определение метрики, план и результаты проверок: так он оценивает не синтаксис, а логику. Хороший вопрос на ревью — «что здесь означает одна строка и почему сумма не может задвоиться». Если на него нет быстрого ответа, запрос ещё не готов.

Отдельно — вопрос данных. Перед тем как отправлять модели примеры строк, убедитесь, что это разрешено политикой компании. Для написания запроса почти всегда достаточно схемы и нескольких обезличенных строк; персональные данные клиентов, суммы на счетах и зарплаты во внешний сервис без согласования отправлять нельзя.

Термины

  • Гранулярность — что означает одна строка результата: клиент, заказ, день, клиент в месяц.
  • Джойн (JOIN) — соединение таблиц по ключу; внутренний оставляет только совпадения, левый сохраняет все строки основной таблицы.
  • Размножение строк — рост числа строк после джойна из-за отношения «один ко многим».
  • CTE — общее табличное выражение, именованный промежуточный шаг запроса.
  • Оконная функция — расчёт по группе строк без их схлопывания: ранги, накопительные итоги, сравнение с предыдущей строкой.
  • Полуоткрытый интервал — период, где начало включено, а конец нет.
  • Контрольная сумма — итог, который считается двумя независимыми способами и должен совпасть.

Чек-лист перед тем, как отправить цифру

  1. 01Метрика записана словами и согласована с тем, кто будет её использовать.
  2. 02Модели переданы диалект, схема, связи и смысл неочевидных полей.
  3. 03Гранулярность результата описана и проверена на уникальность.
  4. 04Модель сначала описала план, и он соответствует задаче.
  5. 05Запрос разбит на шаги, каждый шаг выполнен и проверен.
  6. 06Каждый джойн проверен на размножение и потерю строк.
  7. 07Правила исключения дублей, тестовых и отменённых записей явные и применились.
  8. 08Обработка NULL соответствует бизнес-смыслу метрики.
  9. 09Границы периода — полуоткрытый интервал, часовой пояс и нужная дата учтены.
  10. 10Оконные функции проверены на разбиение, сортировку и рамку.
  11. 11Итог сверен с независимым источником, крайние случаи проверены.
  12. 12Итоговые числа взяты из результата запроса, а не из текста модели.
  13. 13Запрос сохранён вместе с определением метрики и проверками.

Итог

ИИ снимает с аналитика необходимость помнить синтаксис и заметно ускоряет написание запросов. Но правильность цифры по-прежнему обеспечивает человек: точное определение метрики, контроль гранулярности, проверка джойнов, NULL и периодов, сверка с источником. Сделайте эти проверки привычкой — и модель станет надёжным инструментом, а не источником красивых ошибок.

Частые вопросы

Синтаксису — чаще всего да, логике — только после проверки. Запрос может выполниться без ошибок и при этом посчитать неправильно из-за джойна, NULL или границ периода.

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

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

Чаще всего из-за границ периода, часового пояса, выбора даты (создания или оплаты) или разных правил исключения записей. Сверьте дневную разбивку и определение метрики.

Только если это разрешено политикой компании. Для написания запроса обычно достаточно схемы и нескольких обезличенных строк.

Да, на уровне чтения и понимания. Без этого невозможно заметить логическую ошибку в запросе, который выглядит правильным.

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

Все статьи
ИИ-аналитика · MAX

Анализ отзывов, звонков и переписок с ИИ: как превратить текст в таблицу и проверить точность

Анализ отзывов с помощью ИИ: как превратить отзывы, звонки и переписки в таблицу, замерить точность классификации и работать с узбекским и смешанным языком.

15 мин чтения
Заявка

Начнём с разговора.

Оставьте контакт — свяжемся, разберём вашу задачу и честно скажем, какой уровень вам подходит. Если не подходит ни один — так и скажем.

Или напишите напрямую

Отвечаем в рабочее время в течение 30 минут.

Что интересует

Без предоплаты и без обязательств. Отказаться можно на любом шаге.

Нажимая кнопку, вы соглашаетесь на обработку персональных данных в соответствии с политикой конфиденциальности.