SQL-запрос может выполниться без ошибки и всё равно завысить итог. Например, если у заказа две товарные строки и две оплаты, прямое соединение обеих таблиц даст четыре комбинации. Сумма повторится, хотя названия столбцов и синтаксис выглядят правильно.
Поэтому запрос к нейросети лучше начинать не с «сделай отчёт о продажах», а со схемы таблиц, правил расчёта и ожидаемого результата на небольшом наборе данных. Ниже готовый промпт и воспроизводимый пример для SQLite. Редакционные запросы действительно выполнены в SQLite 3.53.1; это проверка примера, а не сравнение чат-сервисов.
Что передать нейросети кроме названий таблиц
Укажите диалект SQL. Синтаксис дат, приведения типов и некоторые функции отличаются между SQLite, PostgreSQL и другими СУБД. Добавьте ключи и связи: один заказ может иметь несколько товаров и несколько оплат. Отдельно объясните, что считается одной строкой итогового отчёта.
Следующий слой — правила предметной области. «Оплачено» может означать наличие успешного платежа, сумму поступлений за месяц или сумму всех оплат по заказам, созданным в этом месяце. Это разные отчёты. Если в схеме нет даты платежа, запрос не сможет честно посчитать месячный денежный поток.
Передавайте учебные или разрешённые обезличенные данные. Пароль подключения, токены и доступ к рабочей базе для подготовки SELECT не нужны. Выполняйте результат сначала на тестовой копии с правами чтения; текстовая просьба «не меняй данные» не заменяет реальные ограничения доступа.
Готовый промпт для отчёта по заказам
Подготовь три SELECT-запроса для SQLite. Не меняй данные.
Сначала проверь, достаточно ли схемы и правил. Если нет,
задай конкретный вопрос, а не выбирай бизнес-правило за меня.
Схема:
orders(id INTEGER PRIMARY KEY, created_at TEXT NOT NULL,
status TEXT NOT NULL, customer_id TEXT);
order_items(id INTEGER PRIMARY KEY, order_id INTEGER NOT NULL,
qty INTEGER NOT NULL, price_cents INTEGER NOT NULL);
payments(id INTEGER PRIMARY KEY, order_id INTEGER NOT NULL,
amount_cents INTEGER NOT NULL, status TEXT NOT NULL).
order_id в обеих дочерних таблицах ссылается на orders.id.
У заказа может быть несколько товаров и несколько платежей.
Деньги хранятся целыми копейками. Даты — UTC, текст одинакового
формата YYYY-MM-DD HH:MM:SS, без смещений и долей секунды.
Выбери только completed-заказы, созданные с 2026-10-01 00:00:00
включительно до 2026-11-01 00:00:00 не включительно.
Стоимость заказа = SUM(qty * price_cents).
Оплачено = сумма всех платежей status='posted' этого заказа.
Отрицательная сумма — возврат. Включай её в SUM со знаком
минус; повторно знак не меняй.
failed не учитывать. Отсутствие posted-платежей даёт ноль.
customer_id=NULL не исключает заказ.
Отсутствие товарных строк даёт стоимость 0.
Если подходящих заказов нет, общий итог — одна строка
с тремя нулями.
Это НЕ денежный поток за октябрь: даты платежа в схеме нет.
1. По одной строке на заказ: id, goods_cents, paid_cents.
2. Общий итог: order_count, goods_cents, paid_cents.
3. Заказы без единой строки posted-платежа: id, customer_id.
Нулевая сумма оплат не равна отсутствию платежей.
Не допускай размножения сумм при JOIN двух дочерних таблиц.
Объясни уровень агрегации и обработку отсутствующих строк.
Сортируй списки по id. После SQL предложи граничные проверки.
К этому запросу приложите небольшой набор INSERT из тестового файла. Для собственной базы замените названия и правила, но не убирайте пояснения про связи и значение итоговых сумм.
Проверяем результат на семи заказах
В учебном наборе семь заказов. Четыре завершённых попадают в октябрь; ещё один создан за секунду до начала месяца, один — точно в начале ноября, один отменён. У заказа 101 две товарные строки и две оплаты, у 102 есть возврат, у 103 неизвестен клиент и есть только неуспешный платёж. У 107 платежей нет вовсе.
Ожидания зафиксированы до выполнения запросов. Суммы ниже переведены из целых копеек в рубли только для чтения.
| Заказ | Стоимость | Оплачено с учётом возврата |
|---|---|---|
| 101 | 250 ₽ | 250 ₽ |
| 102 | 300 ₽ | 250 ₽ |
| 103 | 100 ₽ | 0 ₽ |
| 107 | 100 ₽ | 0 ₽ |
Все три запроса дали ожидаемый результат: четыре заказа, стоимость 750 ₽, оплаты 500 ₽. В список без проведённых платежей вошли 103 и 107. Заказ с неизвестным клиентом не потерялся; сентябрьская и ноябрьская границы и отменённый заказ исключены.
Эти цифры проверяют именно сформулированные правила. Они не означают, что магазину «поступило 500 ₽ в октябре»: в учебной таблице нет времени платежей.
Как поймать размножение JOIN
Намеренно ошибочный редакционный пример напрямую соединяет товары и оплаты заказа 101:
SELECT SUM(i.qty * i.price_cents), SUM(p.amount_cents)
FROM orders o
JOIN order_items i ON i.order_id = o.id
JOIN payments p ON p.order_id = o.id
WHERE o.id = 101 AND p.status = 'posted';
Он вернул 500 ₽ и 500 ₽ вместо 250 ₽ и 250 ₽. Причина в сочетании двух товаров с двумя платежами. Это не ошибка, которую мы наблюдали у конкретной модели, а специально составленный контрпример.
Надёжный для этой схемы подход — сначала отдельно получить одну сумму товаров и одну сумму платежей на заказ, затем соединить эти результаты с orders. После этого каждой строке заказа соответствует не больше одной строки из каждой агрегированной таблицы.
WITH item_totals AS (
SELECT order_id, SUM(qty * price_cents) AS goods_cents
FROM order_items GROUP BY order_id
), payment_totals AS (
SELECT order_id, SUM(amount_cents) AS paid_cents
FROM payments WHERE status = 'posted' GROUP BY order_id
)
SELECT o.id, COALESCE(i.goods_cents, 0) AS goods_cents,
COALESCE(p.paid_cents, 0) AS paid_cents
FROM orders o
LEFT JOIN item_totals i ON i.order_id = o.id
LEFT JOIN payment_totals p ON p.order_id = o.id
WHERE o.status = 'completed'
AND o.created_at >= '2026-10-01 00:00:00'
AND o.created_at < '2026-11-01 00:00:00'
ORDER BY o.id;
Заменять SUM на SUM(DISTINCT …) вслепую нельзя. Две настоящие оплаты по 100 ₽ должны дать 200 ₽, но DISTINCT оставит одну сумму и вернёт 100 ₽. В отдельной контрольной проверке мы получили именно эту разницу.
Промпт для проверки готового SQL
Проверь запрос по указанным схеме и бизнес-правилам.
Не ограничивайся синтаксисом. Для каждого JOIN укажи,
сколько строк он может дать на один заказ.
Проверь возвраты, отсутствие оплат, customer_id=NULL,
границы месяца и отменённые заказы.
Отдельно различай отсутствие платежей и нулевую сумму.
Если предлагаешь исправление, объясни, какой конкретный
контрпример оно исправляет. Не называй запрос проверенным,
если он не выполнялся. Не придумывай результат выполнения.
Ответ модели с объяснением полезен, но не заменяет запуск. Скачать комплект к статье (ZIP): setup.sql, три SELECT, заранее подготовленные ожидания и скрипт проверки. Скрипт создаёт только учебную базу в памяти; перед отчётами включает режим query_only.
Что проверить перед применением к своим данным
Сопоставьте вручную несколько заказов с разным числом товаров и платежей. Добавьте возврат на всю сумму: он даст нулевой итог, но не должен попасть в список «платежей не было». Проверьте пустой период, заказ без товаров и две одинаковые по сумме оплаты.
В примере сравнение дат текстом работает потому, что все значения имеют одинаковый формат и UTC. Для смешанных часовых поясов или другого хранения дат правило нужно изменить. А если отчёт нужен по месяцу фактической оплаты, сначала добавьте соответствующее поле и согласуйте фильтр, а не просите нейросеть угадать отсутствующие данные.
Источники
- SQLite SELECT: https://www.sqlite.org/lang_select.html
- SQLite Aggregate Functions: https://www.sqlite.org/lang_aggfunc.html
- SQLite Date And Time Functions: https://www.sqlite.org/lang_datefunc.html
