Кейс: фильтр поверх UNION
Ситуация
Рекурсивный запрос собирал основную иерархию и добавлял к ней связанные записи через UNION. Динамические фильтры работали для основной ветки, но часть строк из второй ветки попадала в результат в обход условий.
Причина
WHERE находился внутри первого SELECT. Его область действия заканчивалась до UNION:
SELECT id, state
FROM primary_items
WHERE state = :state
UNION
SELECT id, state
FROM related_items;Фильтр применялся только к primary_items.
Мой вклад
Я сначала сформировал общий набор, а затем применил условия к нему:
SELECT *
FROM (
SELECT id, state
FROM primary_items
UNION
SELECT id, state
FROM related_items
) AS all_items
WHERE state = :state
ORDER BY id;Динамический предикат был перенесён во внешний запрос, поэтому стал одинаковым для обеих веток.
Результат
Фильтрация начала соответствовать контракту метода: условие применяется ко всему результату, независимо от источника строки.
Как я проверяю такой запрос
- отдельно выполняю каждую ветку;
- добавляю строки, которые должны быть отсеяны во второй ветке;
- проверяю различие между
UNIONиUNION ALL; - смотрю план внешнего фильтра и стоимость дедупликации;
- параметризую значения, а не собираю их строковой конкатенацией.