В КУРСЕ?

Разбираемся в теме

SQL-запрос стал понятнее с CTE, но сумма удвоилась: где ошибка?

Общие табличные выражения, или CTE, помогают разделить сложный SQL-запрос на именованные части. Но аккуратные названия не защищают от неверного соединения данных. Особенно опасно объединять несколько наборов, в каждом из которых одному объекту соответствует много строк. Рассмотрим, как заметить такую ошибку на маленьком примере и спроектировать проверяемые этапы расчёта.

Уточните смысл одной строки

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

Агрегируйте наборы до общего уровня

Если итоговая строка должна описывать заказ, сначала отдельно рассчитайте стоимость его позиций и сумму платежей. Каждый промежуточный результат должен содержать не более одной строки на идентификатор заказа. Затем соедините их с набором заказов. В примере позиции 40 и 60 дают стоимость 100, а платежи 30 и 70 дают оплату 100. При прямом соединении двух подробных наборов обе суммы станут 200. Раздельное агрегирование сохраняет правильный смысл чисел и упрощает проверку каждого этапа.

Проверьте отсутствующие данные и условия отбора

Заказ без платежей не должен исчезнуть, если задача состоит в отчёте по всем заказам. Для такой связи подходит левое соединение, но отсутствие совпадения создаёт NULL в полях присоединённого результата. Превращать его в ноль допустимо только при подходящем смысле показателя: отсутствие сведений и подтверждённое отсутствие оплаты не всегда равнозначны. Условия отбора тоже важны. Фильтр по правой таблице после левого соединения может удалить строки без совпадений и изменить состав отчёта.

Оцените выполнение отдельно от структуры

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

Попробуйте на практике

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

  1. Создайте пример заказа с двумя позициями стоимостью 40 и 60 и двумя платежами 30 и 70. Запишите ожидаемые итоги до любых соединений.
  2. Перечислите четыре пары, возникающие при соединении позиций с платежами по заказу. Посчитайте суммы и объясните повторение каждого значения.
  3. Составьте два промежуточных результата: сумма позиций по заказу и сумма платежей по заказу. Проверьте уникальность идентификатора в каждом.
  4. Добавьте второй заказ с одной позицией стоимостью 50 и без платежей. Определите, должен ли он присутствовать в отчёте и что означает отсутствие оплаты в этой учебной модели.
  5. Соедините результаты с обоими заказами и сравните с ожиданиями. Отдельно проверьте вариант с двумя одинаковыми суммами позиций, чтобы обнаружить ошибочное удаление повторов.

Как проверить результат. Для первого заказа стоимость и оплата равны 100, а не 200. Второй заказ сохранён; его неопределённое или нулевое значение оплаты трактуется явно. Одинаковые суммы разных позиций не исчезают.

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

Можно ли исправить удвоение с помощью SUM DISTINCT?

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

Каждому действию обязательно нужен отдельный CTE?

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

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

Зарегистрируйтесь, чтобы уточнить возможность доступа к этому материалу

Зарегистрироваться
← К списку материалов