Уточните смысл одной строки
Перед написанием запроса определите, что представляет строка каждого исходного набора. В таблице заказов это может быть один заказ, в позициях одна товарная строка, в платежах одна операция. Идентификатор заказа присутствует везде, но не везде уникален. Если у одного заказа две позиции и два платежа, соединение обоих наборов по заказу даст четыре сочетания. Суммы позиций и платежей повторятся. Ошибка находится в отношениях между данными, поэтому дополнительный CTE вокруг готового соединения её не устранит.
Агрегируйте наборы до общего уровня
Если итоговая строка должна описывать заказ, сначала отдельно рассчитайте стоимость его позиций и сумму платежей. Каждый промежуточный результат должен содержать не более одной строки на идентификатор заказа. Затем соедините их с набором заказов. В примере позиции 40 и 60 дают стоимость 100, а платежи 30 и 70 дают оплату 100. При прямом соединении двух подробных наборов обе суммы станут 200. Раздельное агрегирование сохраняет правильный смысл чисел и упрощает проверку каждого этапа.
Проверьте отсутствующие данные и условия отбора
Заказ без платежей не должен исчезнуть, если задача состоит в отчёте по всем заказам. Для такой связи подходит левое соединение, но отсутствие совпадения создаёт NULL в полях присоединённого результата. Превращать его в ноль допустимо только при подходящем смысле показателя: отсутствие сведений и подтверждённое отсутствие оплаты не всегда равнозначны. Условия отбора тоже важны. Фильтр по правой таблице после левого соединения может удалить строки без совпадений и изменить состав отчёта.
Оцените выполнение отдельно от структуры
CTE помогает читать запрос, но не обещает автоматического ускорения. Способ выполнения зависит от системы, версии и самого выражения. Например, в PostgreSQL некоторые нерекурсивные CTE без побочных эффектов могут объединяться с внешним запросом, а другие материализуются. Поэтому сначала докажите корректность на небольшом наборе, затем изучайте план выполнения в своей среде. Не добавляйте принудительные указания оптимизатору только ради привычного шаблона. Более короткий или визуально аккуратный запрос может выполнять больше работы.
Попробуйте на практике
Проверьте расчёт по заказам на бумаге, а при наличии учебной базы воспроизведите те же данные в запросе только для чтения.
- Создайте пример заказа с двумя позициями стоимостью 40 и 60 и двумя платежами 30 и 70. Запишите ожидаемые итоги до любых соединений.
- Перечислите четыре пары, возникающие при соединении позиций с платежами по заказу. Посчитайте суммы и объясните повторение каждого значения.
- Составьте два промежуточных результата: сумма позиций по заказу и сумма платежей по заказу. Проверьте уникальность идентификатора в каждом.
- Добавьте второй заказ с одной позицией стоимостью 50 и без платежей. Определите, должен ли он присутствовать в отчёте и что означает отсутствие оплаты в этой учебной модели.
- Соедините результаты с обоими заказами и сравните с ожиданиями. Отдельно проверьте вариант с двумя одинаковыми суммами позиций, чтобы обнаружить ошибочное удаление повторов.
Как проверить результат. Для первого заказа стоимость и оплата равны 100, а не 200. Второй заказ сохранён; его неопределённое или нулевое значение оплаты трактуется явно. Одинаковые суммы разных позиций не исчезают.
Частые вопросы
Можно ли исправить удвоение с помощью SUM DISTINCT?
Такой приём суммирует разные значения, а не разные реальные позиции. Две законные строки одинаковой стоимости могут ошибочно превратиться в одну. Исправлять нужно уровень детализации и условия соединения.
Каждому действию обязательно нужен отдельный CTE?
Нет. Именованный этап полезен, когда у него ясная задача и проверяемый результат. Слишком мелкое разбиение может затруднить чтение. Выбирайте структуру, которая помогает объяснить расчёт и проверить его ограничения.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.