Как написать SQL-запрос нейросетью и проверить результат на учебной базе
Редакция MostAI7 мин
Чтобы получить SQL-запрос от нейросети, передайте ей диалект базы, схему таблиц и точное правило расчёта. Затем выполните полученный SELECT на отдельной учебной базе и сравните результат с заранее известным ответом. Объяснение модели не заменяет эту проверку: запрос может запускаться и всё равно считать не то.
Мы сделали три запроса к бесплатному ChatGPT в MostAI 24 октября 2026 года. Модель получила схему вымышленной SQLite-базы, а мы выполнили её ответы на этой базе. В первом результате вместо двух нулей появились NULL. Второй запрос не выполнился из-за неверного псевдонима. Третий вернул нужных клиентов, но не зафиксировал запрошенную сортировку.
Это небольшой разбор одного опыта, а не рейтинг нейросетей и не оценка их точности на всех SQL-задачах. Ни рабочую базу, ни пароли, ни настоящие данные покупателей в чат мы не передавали.
Что подготовить до запроса
Нужны три вещи: схема, вопрос и ожидаемый результат на нескольких понятных строках. Укажите SQLite, PostgreSQL или другую СУБД прямо в запросе. SQL похож между системами, но функции и поведение отдельных конструкций различаются. Наши результаты проверены только в SQLite.
Опишите смысл полей. Например, total_cents хранит полную сумму заказа в копейках, refund_cents хранит возврат. Нулевая сумма не означает отсутствие заказа. Отменённый заказ не входит в оплаченные. Такие правила модель не должна угадывать по имени колонки.
Если нужен общий порядок проверки файлов и кода, посмотрите разбор кода от нейросети. Здесь задача уже: проверить конкретный SELECT, количество строк и сумму на известной базе.
Наша учебная база и ручной ответ
В базе пять вымышленных клиентов, шесть заказов и семь позиций. Заказ 101 имеет две позиции. У Бориса есть отменённый сентябрьский заказ и оплаченный октябрьский. У Веры оплаченный заказ с нулевой суммой. У Глеба оплаченный заказ полностью возвращён. У Даши заказов нет.
Все даты в этом опыте записаны текстом YYYY-MM-DD без времени. Деньги хранятся целыми числами в копейках. Вопрос относится к сентябрю 2026; заказ от 1 октября не входит. Это специально выбранные условия учебного набора, а не универсальная схема магазина.
Схема, которую получила модель:
CREATE TABLE customers(id INTEGER PRIMARY KEY,name TEXT NOT NULL);
CREATE TABLE orders(id INTEGER PRIMARY KEY,customer_id INTEGER NOT NULL,status TEXT NOT NULL,order_date TEXT NOT NULL,total_cents INTEGER NOT NULL,refund_cents INTEGER NOT NULL DEFAULT 0);
CREATE TABLE order_items(id INTEGER PRIMARY KEY,order_id INTEGER NOT NULL,sku TEXT NOT NULL,qty INTEGER NOT NULL,unit_price_cents INTEGER NOT NULL);
До ответов модели мы составили ожидаемую таблицу. Чистая сумма здесь равна total_cents минус refund_cents. Нулевой и полностью возвращённый paid заказ входят в количество.
| id | Клиент | Оплаченных заказов | Чистая сумма, коп. |
|---|---|---|---|
| 1 | Аня | 2 | 4500 |
| 2 | Борис | 0 | 0 |
| 3 | Вера | 1 | 0 |
| 4 | Глеб | 1 | 0 |
| 5 | Даша | 0 | 0 |
Сумма всех пяти строк: 4500 копеек, то есть 45 рублей. Клиенты без paid заказа за сентябрь:Борис и Даша. Эти ответы относятся только к вымышленной базе.
Первый запрос: клиенты без заказов должны остаться
Ниже полный фактически отправленный промт. Схема повторяется намеренно: каждый из трёх запросов был самостоятельным.
Работаем только на вымышленной локальной учебной SQLite-базе. Верни JSON без markdown: {"sql":"один SELECT либо WITH SELECT","explanation":"краткое объяснение"}. Ничего не изменяй, не подключай внешние файлы, не используй расширения. Диалект SQLite. Даты хранятся ISO-текстом.
Схема:
CREATE TABLE customers(id INTEGER PRIMARY KEY,name TEXT NOT NULL);
CREATE TABLE orders(id INTEGER PRIMARY KEY,customer_id INTEGER NOT NULL,status TEXT NOT NULL,order_date TEXT NOT NULL,total_cents INTEGER NOT NULL,refund_cents INTEGER NOT NULL DEFAULT 0);
CREATE TABLE order_items(id INTEGER PRIMARY KEY,order_id INTEGER NOT NULL,sku TEXT NOT NULL,qty INTEGER NOT NULL,unit_price_cents INTEGER NOT NULL);
Вопрос:
Покажи всех клиентов, количество оплаченных заказов за сентябрь2026 и их чистую сумму в копейках. Чистая сумма=total_cents-refund_cents. Оплаченный заказ с суммой0 учитывается в количестве, полностью возвращённый paid заказ тоже учитывается, чистая сумма0. Клиентов без оплаченных сентябрьских заказов сохрани с нулями. Колонки id,name,paid_orders,net_cents; сортировка id.
Фактический SQL в ответе модели:
WITH customer_orders AS (SELECT c.id, c.name, COUNT(o.id) AS paid_orders, SUM(o.total_cents - o.refund_cents) AS net_cents FROM customers c LEFT JOIN orders o ON c.id = o.customer_id AND o.status = 'paid' AND o.order_date BETWEEN '2026-09-01' AND '2026-09-30' GROUP BY c.id, c.name) SELECT id, name, paid_orders, net_cents FROM customer_orders ORDER BY id;
Запрос выполнился. У Ани вернулись 2 заказа и 4500 копеек, у Веры и Глеба по одному заказу и 0 копеек. Но у Бориса и Даши net_cents оказался NULL, а не 0. Модель при этом написала в объяснении, что сохранила клиентов «с нулями».
Причина видна в агрегате. Для клиента без подходящей строки SUM не получает значений, отличных от NULL и возвращает NULL. Это поведение описано в документации SQLite о SUM (откроется в новой вкладке). Оно отличается от бизнес-правила нашего отчёта, где нужен числовой ноль.
Мы отдельно исправили запрос. Это авторская правка, а не ещё один ответ модели:
SELECT c.id,c.name,COUNT(o.id) AS paid_orders,COALESCE(SUM(o.total_cents-o.refund_cents),0) AS net_cents FROM customers c LEFT JOIN orders o ON o.customer_id=c.id AND o.status='paid' AND o.order_date>='2026-09-01' AND o.order_date<'2026-10-01' GROUP BY c.id,c.name ORDER BY c.id;
COALESCE подставляет 0 вместо отсутствующей суммы. COUNT(o.id) считает подходящие заказы, а не строку клиента, оставшуюся после LEFT JOIN. Условия paid и периода находятся в ON: так клиенты без заказов остаются в результате.
Исправленный запрос действительно вернул все пять строк ручного эталона. Условие дат мы записали как интервал от 1 сентября включительно до 1 октября исключительно. Исходный BETWEEN в этом опыте не был ошибкой: даты здесь без времени. Не переносите этот вывод автоматически на колонку со временем и часовым поясом.
Второй запрос: сумма после JOIN
Когда к заказам присоединяют позиции, один заказ появляется несколько раз. В нашем наборе у заказа 101 две позиции. Если после такого JOIN просто сложить total_cents-refund_cents, получится 7500 копеек вместо 4500.
Это отдельно написанный нами пример ошибки, не фактический ответ ChatGPT:
SELECT SUM(o.total_cents-o.refund_cents) AS net_cents FROM orders o JOIN order_items i ON i.order_id=o.id WHERE o.status='paid' AND o.order_date>='2026-09-01' AND o.order_date<'2026-10-01';
Он выполнился и вернул 7500. Поэтому отсутствие ошибки выполнения ещё не доказывает правильность суммы.
Второй полный промт к модели:
Работаем только на вымышленной локальной учебной SQLite-базе. Верни JSON без markdown: {"sql":"один SELECT либо WITH SELECT","explanation":"краткое объяснение"}. Ничего не изменяй, не подключай внешние файлы, не используй расширения. Диалект SQLite. Даты хранятся ISO-текстом.
Схема:
CREATE TABLE customers(id INTEGER PRIMARY KEY,name TEXT NOT NULL);
CREATE TABLE orders(id INTEGER PRIMARY KEY,customer_id INTEGER NOT NULL,status TEXT NOT NULL,order_date TEXT NOT NULL,total_cents INTEGER NOT NULL,refund_cents INTEGER NOT NULL DEFAULT 0);
CREATE TABLE order_items(id INTEGER PRIMARY KEY,order_id INTEGER NOT NULL,sku TEXT NOT NULL,qty INTEGER NOT NULL,unit_price_cents INTEGER NOT NULL);
Вопрос:
Посчитай суммарную чистую выручку paid заказов за сентябрь2026 в копейках. Представь, что аналитик присоединил order_items для последующего анализа: покажи безопасный способ получить общую сумму, которая не задвоится из-за нескольких позиций в заказе. Итог одна колонка net_cents, одна строка. Не дели на100.
Фактический ответ:
WITH order_totals AS (SELECT o.id, SUM(i.qty * i.unit_price_cents) AS total_price_cents FROM orders o JOIN order_items i ON o.id = i.order_id WHERE o.status = 'paid' AND o.order_date BETWEEN '2026-09-01' AND '2026-09-30' GROUP BY o.id) SELECT SUM(total_price_cents - o.refund_cents) AS net_cents FROM order_totals o JOIN orders o2 ON o.id = o2.id;
SQLite остановил выполнение с сообщением no such column: o.refund_cents. Во внешнем SELECT имя o относится к CTE order_totals. В нём есть id и total_price_cents, но нет refund_cents. Заказы во внешнем JOIN названы o2. Вычисленной чистой суммы этот ответ не дал.
Мы написали свой вариант. Сначала свели позиции к одной строке на заказ, затем присоединили этот результат к заказам и сложили исходную сумму минус возврат. В реальном отчёте присоединённое количество может понадобиться для соседних колонок; для одной общей суммы JOIN вообще можно убрать.
WITH item_counts AS (SELECT order_id,COUNT(*) AS item_rows FROM order_items GROUP BY order_id) SELECT SUM(o.total_cents-o.refund_cents) AS net_cents FROM orders o LEFT JOIN item_counts i ON i.order_id=o.id WHERE o.status='paid' AND o.order_date>='2026-09-01' AND o.order_date<'2026-10-01';
Этот вариант вернул одну строку net_cents=4500. Не пытайтесь лечить любое задвоение через SUM(DISTINCT сумма): два разных заказа могут иметь одинаковую сумму. Убирать нужно лишние строки на уровне правильного ключа заказа.
Третий запрос: кто не заказывал в сентябре
Полный промт:
Работаем только на вымышленной локальной учебной SQLite-базе. Верни JSON без markdown: {"sql":"один SELECT либо WITH SELECT","explanation":"краткое объяснение"}. Ничего не изменяй, не подключай внешние файлы, не используй расширения. Диалект SQLite. Даты хранятся ISO-текстом.
Схема:
CREATE TABLE customers(id INTEGER PRIMARY KEY,name TEXT NOT NULL);
CREATE TABLE orders(id INTEGER PRIMARY KEY,customer_id INTEGER NOT NULL,status TEXT NOT NULL,order_date TEXT NOT NULL,total_cents INTEGER NOT NULL,refund_cents INTEGER NOT NULL DEFAULT 0);
CREATE TABLE order_items(id INTEGER PRIMARY KEY,order_id INTEGER NOT NULL,sku TEXT NOT NULL,qty INTEGER NOT NULL,unit_price_cents INTEGER NOT NULL);
Вопрос:
Найди клиентов, у которых нет ни одного paid заказа за сентябрь2026. Клиент с paid заказом0 или полностью возвращённым paid заказом имеет заказ и сюда не входит. Отменённый заказ и заказ1октября не подходят. Колонки id,name, сортировка id.
Фактический ответ модели:
WITH paid_orders AS (SELECT o.id, o.customer_id FROM orders o WHERE o.status = 'paid' AND o.order_date LIKE '2026-09-%') SELECT c.id, c.name FROM customers c WHERE c.id NOT IN (SELECT customer_id FROM paid_orders);
На нашей базе он вернул Бориса и Дашу. Значения совпали с эталоном. Но в запросе нет ORDER BY, хотя мы попросили сортировать по id. Порядок двух строк в одном запуске не превращается в обещание порядка следующих выдач. Документация SQLite о SELECT (откроется в новой вкладке) отдельно описывает сортировку результата.
NOT IN здесь не показал ошибку с NULL: в нашей схеме orders.customer_id объявлен NOT NULL. Мы не добавляем такую ошибку к результатам опыта задним числом.
Для итогового примера мы выбрали явную проверку отсутствия заказа и сортировку. Это тоже авторский вариант:
SELECT c.id,c.name FROM customers c WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id=c.id AND o.status='paid' AND o.order_date>='2026-09-01' AND o.order_date<'2026-10-01') ORDER BY c.id;
Он вернул id2 и 5 в указанном порядке. Нулевой оплаченный заказ Веры и полностью возвращённый оплаченный заказ Глеба не сделали их клиентами без заказов.
Как повторить проверку на своей задаче
- Сформулируйте правило обычными словами: что считается заказом, какой период нужен, как учитывать возвраты и пустые группы.
- Подготовьте маленькую отдельную базу с вымышленными строками. Включите нулевой результат, отсутствие строк и связь «один ко многим».
- Запишите ожидаемые строки и суммы до ответа модели.
- Отправьте схему и вопрос, указав диалект. Не передавайте доступ к рабочей базе.
- Прочитайте SQL. Выполните только проверенный запрос в отдельной среде с доступом на чтение.
- Сверьте не только числа, но и состав строк, NULL, единицы измерения и сортировку.
- Если правите SQL сами, храните исходный ответ отдельно. Не выдавайте исправленный вариант за то, что модель сделала с первой попытки.
В нашем опыте ответы выполнялись локально через SQLite с подключением только на чтение и ограничением допустимых операций. Рабочую базу к MostAI мы не подключали. Это не проверка производительности, прав доступа или безопасности конкретной промышленной системы.
Для задачи, где SQL не нужен и достаточно разобрать уже готовую выгрузку, есть отдельный гайд по разбору таблицы. Не стоит строить запрос к базе только ради пары выводов из маленького CSV.
Файлы для повторения
Все имена, заказы и суммы вымышлены. Схема вместе с INSERT позволяет создать отдельную учебную базу. Не выполняйте этот файл в рабочей базе: он создаёт таблицы и заполняет их примерами.
- Схема и вымышленные данные, SQL-текст
- Ручной эталон
- Фактические результаты трёх проверок
- Полный промт q1
- Сырой ответ q1
- Авторский исправленный SQL q1
- Полный промт q2
- Сырой ответ q2
- Авторский исправленный SQL q2
- Полный промт q3
- Сырой ответ q3
- Авторский исправленный SQL q3
Стоимость и границы опыта
Три фактических вызова chatgpt-free стоили 0 ₽. Суммарная стоимость генерации: 0 ₽. Исправления SQL написал автор и выполнил локально; новых запросов к модели для исправления не делали. Это цена данного бесплатного режима, а не обещание бесплатности любой модели или будущего тарифа.
Из трёх ответов нельзя вывести общую долю правильных SQL-запросов. Полезный результат этого опыта: готовая маленькая база, ожидаемые таблицы и способ отличить ошибку выполнения от незаметной ошибки расчёта.
Составьте запрос по учебной схеме
Откройте чат MostAI, выберите бесплатный ChatGPT и укажите диалект, схему и правило расчёта. Выполните ответ на отдельной учебной базе и проверьте строки и суммы.


