Все материалы
AIгайдсредний

Нейросеть для SQL: как писать запросы с ИИ и проверять их

Как писать SQL с нейросетью: что дать модели на вход, какие ошибки она делает и как их поймать. На примерах из эксперимента с 14 запросами к базе авиаперевозок.

КейсПрактика26 сентября 2026 г.10 мин

Аналитик просит нейросеть посчитать средний чек бронирований, в которых есть хотя бы один билет бизнес-класса. Через секунду готов аккуратный запрос: три таблицы, JOIN, фильтр, AVG. Запрос выполняется без ошибок и возвращает 141 063 ₽. Верный ответ — 125 745 ₽: модель соединила бронирования с перелётами, строки задвоились, и дорогие бронирования попали в среднее по нескольку раз. Запрос из реального прогона, который мы провели на учебной базе. Нейросеть пишет SQL хорошо — заметно лучше, чем считает цифры в тексте. Но её запрос — черновик, и эта статья о том, как превратить черновик в результат.

Что нейросеть делает с SQL хорошо, а что — нет

Мы дали четырём моделям OpenAI схему базы авиаперевозок и 14 задач — от «сколько аэропортов в Москве» до «сколько пассажиров летали в августе и не летали в июле». Каждый запрос выполнили и сравнили результат с эталоном, каждую задачу — трижды. Лучшая модель решила все задачи, остальные — от 81 до 98% запросов. Простые запросы с одной таблицей и понятной группировкой не ломались ни разу.

Ошибки собрались в три узнаваемые группы, и все три видны глазом, если знать, куда смотреть.

  • JOIN, который размножает строки, — и раздувает суммы и средние.
  • Догадки о значениях в данных: «Moscow» вместо «Москва», статус не тем словом.
  • Выдуманные столбцы и таблицы, которых нет в схеме.

Ошибка первая: JOIN, который размножает строки

Самая опасная ошибка, потому что запрос выполняется и возвращает правдоподобное число. В задаче про средний чек бронирование нужно было проверить на наличие бизнес-класса, а усреднять по самим бронированиям. Модель соединила таблицы подряд. У одного бронирования несколько билетов, у билета — несколько перелётов, и одно бронирование превратилось в несколько строк. Вместо 83 730 бронирований в среднее попали 107 642 строки, и больше всего повторов было у крупных бронирований на семью — поэтому среднее выросло.

Так написала модель: среднее считается по строкам после JOIN — 141 063 ₽
SELECT ROUND(AVG(b.total_amount)) AS avg_total
FROM bookings b
JOIN tickets t ON b.book_ref = t.book_ref
JOIN ticket_flights tf ON t.ticket_no = tf.ticket_no
WHERE tf.fare_conditions = 'Business';

Как это починить и как заметить

Условие «есть хотя бы один» пишется через EXISTS: тогда каждое бронирование проверяется, но не размножается. Заметить ошибку помогает одна привычка — считать строки до и после соединения. Если вы усредняете бронирования, строк должно быть столько же, сколько бронирований.

Верно: фильтр через EXISTS, одно бронирование — одна строка, 125 745 ₽
SELECT ROUND(AVG(total_amount)) AS avg_total
FROM bookings b
WHERE EXISTS (
  SELECT 1
  FROM tickets t
  JOIN ticket_flights tf USING (ticket_no)
  WHERE t.book_ref = b.book_ref AND tf.fare_conditions = 'Business'
);
Та же ловушка в другой форме

В задаче про загрузку рейсов одна из моделей присоединила к рейсу сразу места в самолёте и посадочные талоны. Строки перемножились, и средняя загрузка вышла 68,5% вместо 35,1%. Правило то же: считайте каждую сущность в отдельном подзапросе и соединяйте уже готовые итоги.

Ошибка вторая: догадки о значениях

Одна из моделей во всех трёх прогонах искала вылеты из Москвы условием city = 'Moscow'. В базе города записаны по-русски, запрос вернул ноль перелётов вместо 7 419 — и ноль, в отличие от раздутого среднего, хотя бы бросается в глаза. Модель не виновата: в схеме, которую мы дали, не было сказано, на каком языке названия. Она угадала — и не угадала.

Лечится это контекстом. Кроме названий столбцов, дайте модели по несколько реальных значений для тех, по которым будет фильтр: города, статусы, типы. Их легко достать одним запросом.

Примеры значений для промпта — по 10 из каждого столбца, по которому фильтруете
SELECT DISTINCT city FROM airports ORDER BY city LIMIT 10;
SELECT status, count(*) FROM flights GROUP BY status;

Ошибка третья: выдуманные столбцы

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

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

PostgreSQL: список таблиц и столбцов для вставки в промпт
SELECT table_name, string_agg(column_name || ' ' || data_type, ', ' ORDER BY ordinal_position) AS columns
FROM information_schema.columns
WHERE table_schema = 'public'
GROUP BY table_name
ORDER BY table_name;

Что давать нейросети на вход

Всё, что модель должна знать, помещается в полстраницы. Эти полстраницы стоит сохранить как шаблон и дополнять после каждой найденной ошибки.

codeПромпт для запроса
СУБД: PostgreSQL 16.
Схема (из information_schema): <вставить>
Зерно: bookings — одно бронирование; tickets — билет пассажира (в бронировании бывает несколько);
ticket_flights — перелёт по билету на рейс (у билета бывает несколько перелётов).
Значения fare_conditions: 'Economy', 'Comfort', 'Business'.

Задача: средняя сумма бронирования среди бронирований, где есть хотя бы один перелёт бизнес-классом.
Ответ: одна колонка avg_total, округлить до рубля.
Проверь, что соединения не размножают бронирования. Верни только запрос.
  • СУБД и её версия: PostgreSQL, ClickHouse, DuckDB. Функции дат и строк в них разные.
  • Схема: таблицы, столбцы, типы — дословно, из information_schema.
  • Зерно каждой таблицы: что означает одна строка. «Одна строка ticket_flights — перелёт пассажира на рейс».
  • Связи: какие столбцы соединяют таблицы и где связь «один ко многим».
  • Примеры значений для столбцов, по которым фильтруете.
  • Какой ответ нужен: названия колонок, одна строка на что, сортировка, округление.
  • Ограничения: «не размножай строки при JOIN», «верни только запрос».

Как проверить запрос, который написала нейросеть

Проверка занимает пару минут и почти всегда находит ошибку, если она есть. Главное — не смотреть на результат как на ответ, пока не проверено, что запрос считает то, что нужно.

Быстрая проверка раздувания: строк и уникальных бронирований должно быть поровну
SELECT count(*) AS rows_after_join, count(DISTINCT b.book_ref) AS bookings
FROM bookings b
JOIN tickets t USING (book_ref)
JOIN ticket_flights tf USING (ticket_no)
WHERE tf.fare_conditions = 'Business';
-- 107 642 строки против 83 730 бронирований: среднее по таким строкам будет неверным
  • Прочитайте запрос сверху вниз и назовите зерно результата: одна строка — это что.
  • После каждого JOIN посчитайте строки: count(*) и count(DISTINCT ключа) должны совпадать там, где сущность не должна размножаться.
  • Сверьте итог с известной цифрой: общая выручка, число заказов, значение из вчерашнего отчёта.
  • Посчитайте одну группу вручную простым фильтром и сравните.
  • Проверьте краевые случаи: NULL, нули, границы дат, записи без пары при LEFT JOIN.
  • Если результат — ноль или пусто, сначала проверьте условия фильтра: не угадала ли модель значение.

Рабочий цикл: от вопроса до проверенной цифры

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

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

Второй шаг — формулировка вопроса в терминах результата: какие колонки, одна строка на что, за какой период, как округлять. «Выручка по городам за август» превращается в «колонки city и revenue, одна строка на город вылета, рейсы с плановым вылетом с 1 по 31 августа включительно». Половина ошибок в эксперименте исчезла бы, если бы условие было записано так же подробно.

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

Как вернуть модели ошибку, чтобы она её исправила

Сообщение «неправильно, исправь» почти никогда не работает: модель переписывает запрос наугад и может сломать то, что было верным. Работает конкретная обратная связь — то, что вы увидели при проверке, числами.

Для раздутого среднего хватит одной фразы с результатом проверочного запроса: после соединений строк стало 107 642, а бронирований с бизнес-классом 83 730, значит, среднее считается по задвоенным строкам; перепиши так, чтобы каждое бронирование учитывалось один раз. Для нуля вместо результата — список реальных значений столбца. Для ошибки базы — её полный текст: сообщения вроде «column does not exist» модели исправляют хорошо, если видят их целиком.

codeОбратная связь, после которой модель исправляет запрос
Запрос выполнился, но результат неверный.
Проверка: после JOIN строк 107 642, уникальных book_ref — 83 730.
Значит, одно бронирование попадает в AVG несколько раз.
Перепиши запрос так, чтобы каждое бронирование учитывалось один раз
(например, через EXISTS), и объясни, что изменилось.

Какие инструменты использовать

Для SQL подходят три вида инструментов, и выбор между ними — вопрос удобства и правил компании, а не качества модели.

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

Какая модель внутри — важно, но меньше, чем кажется. В нашем эксперименте разница между лучшей и худшей моделью в SQL составила 19 процентных пунктов, а доля верных ответов у одной и той же модели при переходе от ответа текстом к запросу росла на 49–68 пунктов (задачи в двух частях разные, но порядок разницы показателен).

Другие задачи с SQL, где нейросеть полезна

Писать запрос по описанию — не единственный сценарий. Часто нейросеть полезнее как собеседник по уже написанному коду.

Сценарии и что проверять
СценарийКак попроситьНа что смотреть
Объяснить чужой запрос«Объясни построчно, что считает этот запрос и какое у результата зерно»Сверьте объяснение с результатом на маленькой выборке
Найти ошибкуЗапрос + текст ошибки базы + схемаИсправление не должно менять смысл запроса
Перевести между диалектами«Перепиши с PostgreSQL на ClickHouse, сохрани результат»Функции дат и деления целых чисел
Ускорить запросЗапрос + план выполнения (EXPLAIN ANALYZE)Результат до и после должен совпадать до строки
Разбить на шаги«Перепиши через CTE, по шагу на смысловую часть»Промежуточные CTE можно выполнить отдельно

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

Что такое text‑to‑SQL? Так называют перевод вопроса на обычном языке в SQL-запрос. Этим занимаются и чат-боты, и специальные функции BI-систем. Качество зависит от того, насколько подробно модели описаны данные: без схемы и примеров значений даже сильная модель будет угадывать.

Безопасно ли отдавать нейросети схему базы? Схема обычно не содержит данных клиентов, но может раскрывать устройство продукта. Проверьте правила компании; если нужно, переименуйте служебные таблицы и оставьте только те, что нужны для задачи. Сами строки для написания запроса модели не нужны.

Какая нейросеть лучше пишет SQL? В нашем эксперименте GPT-5.5 решила все 42 задачи, GPT-5.4 mini ошиблась один раз, GPT-4.1 mini и GPT-4o — заметно чаще. Но даже лучшая модель не знает ваших данных, поэтому проверка нужна при любом выборе.

Можно ли не проверять простые запросы? Простые запросы к одной таблице модели в эксперименте не ломали ни разу. Ошибки начинаются с соединений, условий «хотя бы один» и «не было» и с фильтров по значениям, которые модель не видела. Если в запросе есть JOIN — проверяйте.

Зачем учить SQL, если его пишет нейросеть

Каждую ошибку из этой статьи можно было поймать, только понимая SQL: видеть зерно таблицы, знать, что делает JOIN со связью «один ко многим», отличать EXISTS от соединения. Нейросеть снимает с аналитика набор текста, но не чтение. А читать запрос и замечать в нём ловушку — навык, который тренируется на задачах с проверкой, а не на готовых ответах.

Если SQL пока даётся с трудом, начните с основ: SELECT, фильтры, агрегаты, JOIN. После этого запросы нейросети перестают быть чёрным ящиком.

Материалы по теме

Продолжить чтение
Вся библиотека