I suspect something's stopping the planner from being able to
deduce that (t.o).id is safe to push through a GROUP BY ocd.o
that might be fixable by making it a separate column of its own
Поэтому было создано дополнительно поле order_id из (level0.o).id
SELECT * FROM (
SELECT
sum( level1.group_suma ) OVER( PARTITION BY level1.order_id ) AS total_suma,
*
FROM (
SELECT
sum( level0.item_suma ) AS group_suma,
level0.o,
(level0.o).id as order_id
FROM order_cost_details() level0
GROUP BY level0.o, (level0.o).id, (level0.ic).consumed_period
) level1
) level2
WHERE level2.order_id = 6154 -- works fast
WHERE (level2.o).id = 6154 -- works slow
Как было указано в [ответе на SO](https://stackoverflow.com/a/57004652/4632019)
новое поле решает проблему и планер может использовать значение 6154
для оптимизации.
Но проблема заключается в том, что этот трюк работает только для order_id
В других ситуация мне нужно фильтровать мой набор по agreement_id.
Я переписал запрос:
SELECT * FROM (
SELECT
sum( level1.group_suma ) OVER( PARTITION BY level1.order_id ) AS total_suma,
fn() as new_field
*,
FROM (
SELECT
sum( level0.item_suma ) AS group_suma,
level0.o,
(level0.o).id as order_id
(level0.o).agreement_id as agreement_id
FROM order_cost_details() level0
GROUP BY level0.o, (level0.o).id, (level0.ic).consumed_period
) level1
) level2
WHERE level2.agreement_id = 6154 -- works fast
Получается, что если я хочу, чтобы планер использовал значение в оптимизации,
то я должен выбирать необходимые поля явным образом, пока, со временем,
просто напрасто перечислю их всех.
Но зачем это делать, если я и так передаю все поля по ссылке:
level0.o в результате даёт одно поле, которое содержит остальные
Таким образом поле (level2.o).id и (level0.o).id - это одно и тоже значение в памяти,
грубо говоря - алиас.
Поэтому если я явно обращаюсь к level0, почему PG запрещает мне это делать?
Я совершенно не против ситуации, когда PG может запретить push для новых
полей, которые были созданы в этом уровне, как например:
(level0.o).agreement_id as order_id
Вот есть наши 3 запроса (см. картинку). level0 - исходные данные
и level1, level2 - полученные (derived) данные.
Соответсвенно мы не может делать какие либо манипуляции (например фильтровать)
на level0, если эти данные у нас появились только на level1
В моём примере, если я напишу level2.new_field = 3,
то планер может запросто переписать его на level1.new_field. Это безопасно.
планер не может переписать его на level0.new_field,
т.к. на level0 этого поля нет.
Аналогично: level2.order_id -> level1.order_id. Это безопасно.
планер может переписать его на (level0.o).id,
т.к. на level0 это поле есть и (level0.o).id удовлетворяет условию группировки. (GROUP BY)
Я бы предложил рассматривать эти примеры не как push, а как bubbling.
тогда наше поле id всплывает вот так: id -> order_id -> order_id
А теперь по поводу ссылок. Наше о всплывает так: o -> o -> o
На всех уровнях - это одно и тоже о. Значит если я говорю:
o.id = 6154 или какое либо другие поля по ссылке (я имею ввиду o)
То база данных должна сделать фильтрацию на level0, а потом уже получать данные из верхних уровней.
Я тут говорю должна, потому что я обращаюсь к o по ссылке,
а не как к полученному (derived) полю
Я Вам уже писал — Вы пишете "простыни", которые очень трудно для чего-то использовать! ;( Т.е. используя то, что Вы написали, воспроизвести проблему никто, кроме Вас, не может. Давайте minimal test case, потому что иначе вряд ли это кому-то (влкючая -hackers) будет интересно, понимаете?
Подозреваю, что запор происходит в процессе группировки: она по .o, а .o.id - это уже не значение, а вычисляемое поле. Должно ли так быть и может ли так не быть в общем случае, не знаю. Но поддержу идею, что для разборок необходим компактный тестовый пример с данными. Рекомендую выложить его на dbfiddle.uk, чтобы сразу были видны результаты запросов и можно было поиграть с параметрами.
Обсуждают сегодня