А скрепки прямо под ногами у двери валяются? Если он хотя бы это осознаёт, то так же осознаёт, что отвёртку и скрепки можно попросить у соседей или знакомых, а ключи ну никак не получится.
Несмотря на то, что я очень уважаю ИБ и сам часто настаиваю на соблюдении требований ИБ, у меня скоро на словосочетание "специалист по ИБ" будет идиосинкразия именно из-за "специалистов" после подобных курсов.
Например, я уже больше года пытаюсь добиться от ИБ крупного холдинга ответа на простой вопрос: "Почему, если у меня уже есть доступ через VPN к 22-му порту одного из серверов в DMZ, мне отказывают в доступе через VPN к 22-му порту другого сервера в этом DMZ, и я вынужден ходить к нему при помощи -J через первый сервер?"
Между разными DMZ - та же картина. Только на текущем проекте у меня уже около десятка линков через SSH тоннели с джампами, так как добиться одобрения прямой связности от ИБ пока не удалось.
Без отвертки очень неудобно. Ведь сувальды в нужное положение ставить приходится по очереди. А отвёрткой их удерживать в выставленном положении очень просто.
Так и я про это. Конкуренция для потребителя - только плюс. А монополист без конкуренции, как бы ни был он хорош, всё равно рано или поздно превращается в минус.
Вопрос в стиле, "зачем нужна Mitsubishi, если есть Toyota?" или "зачем нужен Хабр, если есть Пикабу?".
Другое дело, что на данный момент RuTube не только не может предложить что-то, что не может предложить YouTube, но ещё и проигрывает по удобству использования последнему почти по всем статьям, кроме возможности доступа из РФ без лишних телодвижений.
Минусы — сложность на стороне источника (нужно знать всех подписчиков, управлять подключениями, переживать перезапуски потребителей) и риск каскадных сбоев, если один из получателей завис или упал.
А зачем источнику нужно это знать? Забота источника тут минимальна. Остальное делает Debezium, Confluent и Kafka,
каждом из 500 подов
Тогда брокер вынужден дублировать одни и те же сообщения для каждой группы и хранить её offset, что создаёт избыточную нагрузку на брокер и сеть.
С учётом того, что брокер ограничен ~10 тысячами подписчиками, а рекомендованный максимум 2-4 тысячи, то Вам до этого ограничения ещё далеко. А когда доберётесь до этого ограничения, создадите копию топика либо ещё одним Sink, либо копированием из топика в топик.
Итак, мы выбрали pull-модель и решили строить лог изменений прямо в PostgreSQL, в котором мы уже храним слепок данных. Идея — присвоить каждой записи монотонно возрастающий номер версии, а потребителям дать возможность запрашивать «все изменения, случившиеся после версии X».
Зачем изобретать велосипед, да ещё и загружая сервер, если это решается штатными средствами через replication slot? Причём никто не запрещает грузить этим не мастер, а реплики.
Ну и совсем не обязательно держать всё в каждом поде. ValKey вполне в состоянии обслуживать сразу несколько подов с весьма незначительной деградацией производительности, но с существенной экономией памяти.
Так как система дифференциальных уравнений более высокого порядка сводится к системе большего числа уравнений первого порядка, то получается, что поддержка решения дифференциального уравнения 60 порядка эквивалентна поддержке решения системы дифференциальных уравнений первого порядка из 60 уравнений.
Я в МИЭТ на кафедре в конце 80-х подрабатывал, решая именно подобные задачи в целях обработки даннных АФАР. И у меня есть весьма веские основания подозревать, что современные российские АФАР решают системы дифференциальных уравнений именно аналоговым путём, а уже остальные расчеты - цифровым. С учётом того, что в той же Белке свыше полутора тысяч ППМ (приёмно-передающих модулей), такой подход выглядит до сих пор логичным.
С точностью наоборот, появилась возможность использовать Rust на любой архитектуре, не поддерживаемой LLVM. Архитектур, которые не поддерживают C я не встречал. Даже для Paduk есть sdcc.
А это к чему? Вы будто на своей волне не видя, что Вам пишут. Ладно. На пальцах и со скринами.
Я писал:
Ваш ответ:
Когда нужно выбрать N элементов, то быстрая сортировка сильно хуже heap sort
Какое отношение это имеет к порядку сортировки? Особенно с учётом того, что Вы сами написали
(heapsort, TOP-N)
И почему вообще рассматривается исключительно TOP N, да ещё и обязательно без последующих INNER JOIN, фильтрующих выборку? Да и указанные фильтры вполне могут ограничить выборку уже менее, чем количество записей, указанное в LIMIT. Это даже не считая того, что я указал явно для MERGE JOIN, который может повысить производительность запроса, если сортируются количество записей, отличающееся менее, чем на порядок от количества прыжков по индексам в nested loop, которых ещё нет в RAM. А может пользователю нужна гистограмма по десятку значений в PERCENTILE_CONT, а не мало о чём говорящий TOP?
Я уже который раз указываю Вам, что если уж собрались трогать планировщик, то необходимо учитывать все варианты запросов, которые модификация планировщика может затронуть, а вовсе не заниматься "велика у стула ножка- подпилю её немножко".
Ну и игнорировать остальные заданные вопросы и просьбы несколько некрасиво.
Это совсем неверно. Если у него наверху не агрегат, то статистика будет ровно та же, что и если бы не было подзапроса - вычисление селективности умеет в рекурсию.
Такое ощущение, что мы говорим о разных вещах. Приведите Ваш пример подзапроса, который нельзя трансформировать в JOIN, но селективность которого планировщик правильно отображает в EXPLAIN ANALYZE. А то какой-то беспредметный разговор получается.
всё ломается при малейшем изменении внутри такого выражения
Так себе аргумент. Вы уж простите, но с дуру можно ещё не то сломать.
Когда нужно выбрать N элементов, то быстрая сортировка сильно хуже heap sort
Какое отношение это имеет к обсуждаемому случаю?
ручной тюнинг запросов с такими объемами облачных баз, клиентов и приложений я не рассматриваю
То есть, Вы предлагаете тем, кому уже совсем горит переходить с Hana/Oracle/MS SQL на PostgreSQL ждать светлого будущего? Вы сами упоминали SAP. Вот и приведите хотя бы один пример, когда вообще без ручной оптимизации запросов удалось перейти с Hana на PostgreSQL. Даже при переводе ЭТРАН с Oracle на PostgreSQL, в РЖД три-четыре месяца такой ручной оптимизацией занимались только после ввода в эксплуатацию. АСОУП-3 до сих пор продолжают вручную на SQL оптимизировать, хотя переход на PostgreSQL начали ещё в 2022 году. Но там и объёмы на два порядка больше, чем в ЭТРАН.
Никто не спорит с тем, что планировщик в PostgreSQL имеет массу недостатков и его нужно развивать. Но даже поверхностное знакомство с куда более мощными планировщиками Oracle и MS SQL показывает, что при кривой архитектуре БД и тупой ORM, "серебрянной пули" не бывает. И без участия человека на уровне SQL все проблемы не решаемы.
Ежели бы ручной тюнинг был настолько актуален
Я же указал, что Вы видите мир пользователей PostgreSQL очень ограничено. И что странно, я почему-то вижу самые разные профили использования РСУБД, а Вы отказываетесь это делать. Сложно поверить, что Вы действительно не знаете, что для того, чтобы пользователь не ждал результата запроса свыше полсекунды, необходимо порой до этого трансформировать данные не то что секунды, а минуты или десятки минут. А интеграционные B2B данные в размере миллионов строк в ответ на один запрос - вообще обычное дело.
CASE использовать - это точно опасный подход, годящийся для вручную управляемой БД - таких кейсов у меня не бывает.
И опять про себя. Причём ровно в том случае, когда именно у Вас же кривая архитектура БД, которая привела к полиморфным ссылкам. А ведь что стоило иметь три разных поля для разных сущностей или одну таблицу с полем типа сущности?
Я же лишь указал пути, как можно на такой кривой архитектуре выкрутиться, если уже такое нагородили. Выкрутиться здесь и сейчас, а не в каком-то светлом будущем.
Люди приходят ко мне
К чему argumentum ad verecundiam в технической дискуссии? Вы бы хотя бы один повторяемый аргумент в коде привели, вместо обсуждения сферического коня в вакууме, забыв почти обо всех методах соединения таблиц, кроме nested loop.
все это регулируется костами
О которых во множестве случаев планировщик не знает и в принципе знать не может? Ровно ведь, как в Вашем примере, когда статистика по полю item_id говорит лишь о средней температуре по больнице.
Даже в моей практике наберётся десятки примеров, когда даже в теории без ручного управления планировщиком никакие оценки на базе статистик помочь не могут.
Хм, мои клиенты не любят ждать больше 500мс ;) И им все равно, какой размер базы.
миры у нас разные
Вот уж точно миры разные, потому что даже только загрузка данных в CPLEX или Gurobi для предприятия, использующего SAP или OEBS, займет больше времени. И странное заявление про размер БД. При чём тут размер БД, если запросу по постановке нужно перемолоть миллионы записей и никак иначе?
Если речь не о подготовке данных для витрины, а уже о самой витрине, то перенесите данные в тот же ClickHouse, а из PostgreSQL берите только те, которые ещё не попали в него. SAP в Hana именно таким образом и поступает, перенося исторические данные из row store в column store.
так о том и речь, что неоптимальный запрос сканирует 1E9 строк, чтобы выдать сотню.
И где этот запрос в таком же виде, как предоставил я? Так чтобы это легко можно было повторить любому желающему.
Пример, как оптимизировать на 20-30% выборку 68% их 10 млн. строк Вам не интересен, так как у Вас уникальные пользователи, которым это не нужно.
Пример, как оптимизировать выборку сотни строк из этих же 10 миллионов Вам тоже не интересен.
Ну так дайте пример, который интересен.
все построено на драйверах баз данных, rest API, а теперь ещё и AI-generators
А к БД можно обращаться без драйвера? И при чем тут REST? Может еще gRPC и SOAP приплести? )))
1C и правда не встречает, но я и использую его только для русскоязычного сообщества. На английском языке я упоминаю SalesForce, SAP, всякие CRM, Django, Hibernate
Вы действительно думаете, что если есть ORM, то обратится из неё к оптимизированному представлению или функции уже нельзя? Проблема то как раз именно в 1С, которая в отличии от SAP, OEBS, Dynamics и т.п. практически не позволяет создавать представления и пользовательские функции в БД в целях оптимизации. 1С в этом смысле уникальна, скрывая имена таблиц и полей от разработчика и даже не гарантируя, что эти имена вдруг не изменятся при очередной модификации конфигурации.
Вспомнили динозавра!
По крайней мере для поддержки 1С они сделали в разы больше, чем кто-либо другой.
Указанные в тексте доработки достаточно просты, так что судьба двух из них кажется мне вполне перпективной.
Ещё раз и уже на пальцах.
Для начала, nested loop далеко не единственный метод реализации соединения таблиц. И я начал как раз со сценария, где он крайне неэффективен. А из того, что Вы с такими сценариями не сталкиваетесь, совершенно не следует, что их нет у многих других.
1. Result Filter и одностороннее ограничение
Оборачивая выражение в CASE, как я показал выше, мы добиваемся именно того, что условия зависящие от предыдущих связей (внешней группы) будут вычислены и проверены до условий присоединения текущей связи. И опять таки, как я заметил выше, никто не запрещает создать статистики по выражению. В данном случае - по CASE. Причем пользы от такой статистики будет намного больше, чем от статистики только по item_id.
2. Sort Pushdown или предварительная сортировка
Наиболее эффективным выходом из ситуации будет вставка операции сортировки (heapsort, TOP-N) сразу над оператором сканирования базовой таблицы
Во-первых, если в таблице order_lines миллиард строк, то станет только хуже. Во-вторых, в ряде случаев эффективней может оказаться сортировка не по popularity, а по item_id, позволяющая от nested loop перейти к merge join. Даже не смотря на необходимость второй сортировки. Особенно ярко это проявится, если индексы связываемых таблиц ещё на диске, а не в памяти.
3. SubLink relocation или фильтруем как можно раньше
EXISTS-подзапрос, который не получается трансформировать в JOIN
Снова приходим к кривизне запроса. Если подзапрос нельзя трансформировать в JOIN, то и о его селективности планировщик ничего не знает. Только разработчик может запинать планировщик в нужный план, имея представление об этой селективности. Хотя куда логичней сразу написать запрос так, чтобы планировщик мог получить информацию о селективности. Иногда это приходится делать ценой заливки данных в промежуточную нежурналируемую таблицу. И тогда планировщик уже сможет понять, какой же механизм соединения тут лучше применить.
4. Join statistics
Возвращаемся снова к соединению по CASE, который позволяет использовать статистики, если они созданы.
И в качестве вишенки на торт, самое главное. Ни один из перечисленных способов не позволяет планировщику действительно выбрать оптимальный план запроса в моём примере с выборкой десятков строк из миллионов. А вот CASE и создание статистик по ним сразу творят чудеса. Потому что тут уже планировщик знает селективность до вычисления каких-либо фильтров.
Как по мне, то 5 секунд - это тоже очень плохо, если только запросу не нужно для аналитики действительно поднять миллионы строк.
Для игрального сервера, которому в сумме надо переживать ~14 миллионов строк - вполне нормально.
Мой пост всё-таки для случаев, где реальное количество данных, нужное для построения ответа, невелико.
Так тут и разницы не увидите в любом случае, так как она окажется в пределах 10 мс.
вместо многократного сканирования многих таблиц можно ограничиться точечным обращением по месту
Это никто не запрещает, вот только это будет медленней на миллионах записях (у меня получилось уже 16 секунд на тех же 6.8 миллионах из заголовка и столько же суммарно из трёх деталей). На малом количестве, в сотню записей, загнав планировщик в nested loop действительно будет на два порядка быстрее, чем в исходном варианте.
EXPLAIN ANALYZE
SELECT H.id, H.tran_date, H.tp,
COALESCE(D1.name,D2.name,D3.name) AS name
FROM tmp_hdr H
LEFT JOIN tmp_det1 D1 ON H.tp=1 AND D1.id=H.id_det
LEFT JOIN tmp_det2 D2 ON H.tp=2 AND D2.id=H.id_det
LEFT JOIN tmp_det3 D3 ON H.tp=3 AND D3.id=H.id_det
WHERE H.id BETWEEN 100000 AND 100100;
-- Execution Time: 53.233 ms
EXPLAIN ANALYZE
SELECT H.id, H.tran_date, H.tp,
COALESCE(D1.name,D2.name,D3.name) AS name
FROM tmp_hdr H
LEFT JOIN tmp_det1 D1 ON D1.id=CASE WHEN H.tp=1 THEN H.id_det ELSE NULL END
LEFT JOIN tmp_det2 D2 ON D2.id=CASE WHEN H.tp=2 THEN H.id_det ELSE NULL END
LEFT JOIN tmp_det3 D3 ON D3.id=CASE WHEN H.tp=3 THEN H.id_det ELSE NULL END
WHERE H.id BETWEEN 100000 AND 100100;
-- Execution Time: 53.709 ms
EXPLAIN ANALYZE
SELECT H.id, H.tran_date, H.tp,
COALESCE(D1.name,D2.name,D3.name) AS name
FROM tmp_hdr H
LEFT JOIN LATERAL (
SELECT D.name
FROM tmp_det1 D
WHERE H.tp=1 AND D.id=H.id_det
LIMIT 1 ) D1 ON true
LEFT JOIN LATERAL (
SELECT D.name
FROM tmp_det2 D
WHERE H.tp=2 AND D.id=H.id_det
LIMIT 1 ) D2 ON true
LEFT JOIN LATERAL (
SELECT D.name
FROM tmp_det3 D
WHERE H.tp=3 AND D.id=H.id_det
LIMIT 1 ) D3 ON true
WHERE H.id BETWEEN 100000 AND 100100;
-- Execution Time: 0.395 ms
Если заранее известно, что записей десятки, то кто запрещает так переписать запрос?
P.S. Я понимаю, что в рамках 1С такое переписывание запроса может оказаться весьма непростой задачей. Я лишь показал, почему проблемы 1С не встречают активного отклика в PostgreSQL community и, очень часто, остаются в пределах Postgres Pro. С точки зрения community - "внесите изменения в платформу, чтобы она более оптимальные запросы строила, и радуйтесь".
Я понимаю, что возможны варианты, но они относятся больше к запросам, обрабатывающим буквально тысячи записей, где проблема решается масштабированием. А как только речь зайдет хотя бы о сотне тысяч, если не миллионах, то разница будет намного больше. Например, на нескольких миллионах разница набегает уже ~3 секунды или уже свыше 30% провала в производительности.
Чтобы не быть голословным
CREATE TEMP TABLE tmp_hdr (
id serial PRIMARY KEY,
tran_date date NOT NULL,
tp smallint NOT NULL,
id_det integer NOT NULL
);
CREATE TEMP TABLE tmp_det1 (
id serial PRIMARY KEY,
name varchar
);
CREATE TEMP TABLE tmp_det2 (
id serial PRIMARY KEY,
name varchar
);
CREATE TEMP TABLE tmp_det3 (
id serial PRIMARY KEY,
name varchar
);
INSERT INTO tmp_hdr (tran_date, tp, id_det)
SELECT '2026-01-01'::date+G.n/100000*'1 day'::interval AS tran_date,
ceil(random()*3)::smallint AS tp,
ceil(random()*100000)::integer AS id_det
FROM generate_series(0,9999999) G(n);
CREATE INDEX tmp_hdr_tran_date_idx ON tmp_hdr (tran_date, tp, id_det);
INSERT INTO tmp_det1 (name)
SELECT G.n::varchar
FROM generate_series(0,99999) G(n);
INSERT INTO tmp_det2 (name)
SELECT G.n::varchar
FROM generate_series(0,99999) G(n);
INSERT INTO tmp_det3 (name)
SELECT G.n::varchar
FROM generate_series(0,99999) G(n);
EXPLAIN ANALYZE
SELECT H.id, H.tran_date, H.tp,
COALESCE(D1.name,D2.name,D3.name) AS name
FROM tmp_hdr H
LEFT JOIN tmp_det1 D1 ON H.tp=1 AND D1.id=H.id_det
LEFT JOIN tmp_det2 D2 ON H.tp=2 AND D2.id=H.id_det
LEFT JOIN tmp_det3 D3 ON H.tp=3 AND D3.id=H.id_det
WHERE H.tran_date>'2026-02-01'::date;
EXPLAIN ANALYZE
SELECT H.id, H.tran_date, H.tp,
COALESCE(D1.name,D2.name,D3.name) AS name
FROM tmp_hdr H
LEFT JOIN tmp_det1 D1 ON D1.id=CASE WHEN H.tp=1 THEN H.id_det ELSE NULL END
LEFT JOIN tmp_det2 D2 ON D2.id=CASE WHEN H.tp=2 THEN H.id_det ELSE NULL END
LEFT JOIN tmp_det3 D3 ON D3.id=CASE WHEN H.tp=3 THEN H.id_det ELSE NULL END
WHERE H.tran_date>'2026-02-01'::date;
Execution Time: 8112.906 ms против Execution Time: 5180.712 ms
Я бы добавил ещё, что от статистики по полиморфной ссылке по жизни толку мало для планировщика, так как он не знает, на какую же таблицу она ссылается. При этом никто не запрещает создать статистики и по CASE.
SELECT
ol.id,
COALESCE(p.name, g.name, s.name) AS item_name
FROM order_lines ol
LEFT JOIN products p
ON p.id = CASE WHEN ol.type = 'A' THEN ol.item_id ELSE NULL END
LEFT JOIN gift_cards g
ON g.id = CASE WHEN ol.type = 'B' THEN ol.item_id ELSE NULL END
LEFT JOIN subscriptions s
ON s.id = CASE WHEN ol.type = 'C' THEN ol.item_id ELSE NULL END
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.id = ol.order_id AND o.placed_at >= DATE '2024-01-01')
ORDER BY ol.popularity
LIMIT 100;
У меня подобный подход приводит к ~20% улучшения производительности.
А скрепки прямо под ногами у двери валяются? Если он хотя бы это осознаёт, то так же осознаёт, что отвёртку и скрепки можно попросить у соседей или знакомых, а ключи ну никак не получится.
Несмотря на то, что я очень уважаю ИБ и сам часто настаиваю на соблюдении требований ИБ, у меня скоро на словосочетание "специалист по ИБ" будет идиосинкразия именно из-за "специалистов" после подобных курсов.
Например, я уже больше года пытаюсь добиться от ИБ крупного холдинга ответа на простой вопрос: "Почему, если у меня уже есть доступ через VPN к 22-му порту одного из серверов в DMZ, мне отказывают в доступе через VPN к 22-му порту другого сервера в этом DMZ, и я вынужден ходить к нему при помощи -J через первый сервер?"
Между разными DMZ - та же картина. Только на текущем проекте у меня уже около десятка линков через SSH тоннели с джампами, так как добиться одобрения прямой связности от ИБ пока не удалось.
Без отвертки очень неудобно. Ведь сувальды в нужное положение ставить приходится по очереди. А отвёрткой их удерживать в выставленном положении очень просто.
Ну не 10-20 секунд. В детстве, при помощи скрепок и отвёртки, у меня на такой замок уходило минут 5-10 )))
Так и я про это. Конкуренция для потребителя - только плюс. А монополист без конкуренции, как бы ни был он хорош, всё равно рано или поздно превращается в минус.
Вопрос в стиле, "зачем нужна Mitsubishi, если есть Toyota?" или "зачем нужен Хабр, если есть Пикабу?".
Другое дело, что на данный момент RuTube не только не может предложить что-то, что не может предложить YouTube, но ещё и проигрывает по удобству использования последнему почти по всем статьям, кроме возможности доступа из РФ без лишних телодвижений.
Раз уж речь о PostgreSQL и максимальной производительности не в ущерб надёжности, то всё же xfs.
А зачем источнику нужно это знать? Забота источника тут минимальна. Остальное делает Debezium, Confluent и Kafka,
С учётом того, что брокер ограничен ~10 тысячами подписчиками, а рекомендованный максимум 2-4 тысячи, то Вам до этого ограничения ещё далеко. А когда доберётесь до этого ограничения, создадите копию топика либо ещё одним Sink, либо копированием из топика в топик.
Зачем изобретать велосипед, да ещё и загружая сервер, если это решается штатными средствами через replication slot? Причём никто не запрещает грузить этим не мастер, а реплики.
Ну и совсем не обязательно держать всё в каждом поде. ValKey вполне в состоянии обслуживать сразу несколько подов с весьма незначительной деградацией производительности, но с существенной экономией памяти.
Так как система дифференциальных уравнений более высокого порядка сводится к системе большего числа уравнений первого порядка, то получается, что поддержка решения дифференциального уравнения 60 порядка эквивалентна поддержке решения системы дифференциальных уравнений первого порядка из 60 уравнений.
Производная может быть даже от сотен переменных, если источником значений этих переменных выступает ФАР (фазированная антенная решётка)
Сама сборка с нуля была не сильно проще, чем перемычками. Зато все схемы можно было хранить на дискетах, быстро загружать или вносить в них изменения.
В конце 80-х на кафедре схемотехники МИЭТ коммутацию выполняли с ДВК-2. Вместо перемычек - 561КТ3
Я в МИЭТ на кафедре в конце 80-х подрабатывал, решая именно подобные задачи в целях обработки даннных АФАР. И у меня есть весьма веские основания подозревать, что современные российские АФАР решают системы дифференциальных уравнений именно аналоговым путём, а уже остальные расчеты - цифровым. С учётом того, что в той же Белке свыше полутора тысяч ППМ (приёмно-передающих модулей), такой подход выглядит до сих пор логичным.
С точностью наоборот, появилась возможность использовать Rust на любой архитектуре, не поддерживаемой LLVM. Архитектур, которые не поддерживают C я не встречал. Даже для Paduk есть sdcc.
А это к чему? Вы будто на своей волне не видя, что Вам пишут. Ладно. На пальцах и со скринами.
Я писал:
Ваш ответ:
Какое отношение это имеет к порядку сортировки? Особенно с учётом того, что Вы сами написали
И почему вообще рассматривается исключительно TOP N, да ещё и обязательно без последующих INNER JOIN, фильтрующих выборку? Да и указанные фильтры вполне могут ограничить выборку уже менее, чем количество записей, указанное в LIMIT. Это даже не считая того, что я указал явно для MERGE JOIN, который может повысить производительность запроса, если сортируются количество записей, отличающееся менее, чем на порядок от количества прыжков по индексам в nested loop, которых ещё нет в RAM. А может пользователю нужна гистограмма по десятку значений в PERCENTILE_CONT, а не мало о чём говорящий TOP?
Я уже который раз указываю Вам, что если уж собрались трогать планировщик, то необходимо учитывать все варианты запросов, которые модификация планировщика может затронуть, а вовсе не заниматься "велика у стула ножка- подпилю её немножко".
Ну и игнорировать остальные заданные вопросы и просьбы несколько некрасиво.
Такое ощущение, что мы говорим о разных вещах. Приведите Ваш пример подзапроса, который нельзя трансформировать в JOIN, но селективность которого планировщик правильно отображает в EXPLAIN ANALYZE. А то какой-то беспредметный разговор получается.
Так себе аргумент. Вы уж простите, но с дуру можно ещё не то сломать.
Какое отношение это имеет к обсуждаемому случаю?
То есть, Вы предлагаете тем, кому уже совсем горит переходить с Hana/Oracle/MS SQL на PostgreSQL ждать светлого будущего? Вы сами упоминали SAP. Вот и приведите хотя бы один пример, когда вообще без ручной оптимизации запросов удалось перейти с Hana на PostgreSQL. Даже при переводе ЭТРАН с Oracle на PostgreSQL, в РЖД три-четыре месяца такой ручной оптимизацией занимались только после ввода в эксплуатацию. АСОУП-3 до сих пор продолжают вручную на SQL оптимизировать, хотя переход на PostgreSQL начали ещё в 2022 году. Но там и объёмы на два порядка больше, чем в ЭТРАН.
Никто не спорит с тем, что планировщик в PostgreSQL имеет массу недостатков и его нужно развивать. Но даже поверхностное знакомство с куда более мощными планировщиками Oracle и MS SQL показывает, что при кривой архитектуре БД и тупой ORM, "серебрянной пули" не бывает. И без участия человека на уровне SQL все проблемы не решаемы.
Я же указал, что Вы видите мир пользователей PostgreSQL очень ограничено. И что странно, я почему-то вижу самые разные профили использования РСУБД, а Вы отказываетесь это делать. Сложно поверить, что Вы действительно не знаете, что для того, чтобы пользователь не ждал результата запроса свыше полсекунды, необходимо порой до этого трансформировать данные не то что секунды, а минуты или десятки минут. А интеграционные B2B данные в размере миллионов строк в ответ на один запрос - вообще обычное дело.
И опять про себя. Причём ровно в том случае, когда именно у Вас же кривая архитектура БД, которая привела к полиморфным ссылкам. А ведь что стоило иметь три разных поля для разных сущностей или одну таблицу с полем типа сущности?
Я же лишь указал пути, как можно на такой кривой архитектуре выкрутиться, если уже такое нагородили. Выкрутиться здесь и сейчас, а не в каком-то светлом будущем.
К чему argumentum ad verecundiam в технической дискуссии? Вы бы хотя бы один повторяемый аргумент в коде привели, вместо обсуждения сферического коня в вакууме, забыв почти обо всех методах соединения таблиц, кроме nested loop.
О которых во множестве случаев планировщик не знает и в принципе знать не может? Ровно ведь, как в Вашем примере, когда статистика по полю item_id говорит лишь о средней температуре по больнице.
Даже в моей практике наберётся десятки примеров, когда даже в теории без ручного управления планировщиком никакие оценки на базе статистик помочь не могут.
Вот уж точно миры разные, потому что даже только загрузка данных в CPLEX или Gurobi для предприятия, использующего SAP или OEBS, займет больше времени. И странное заявление про размер БД. При чём тут размер БД, если запросу по постановке нужно перемолоть миллионы записей и никак иначе?
Если речь не о подготовке данных для витрины, а уже о самой витрине, то перенесите данные в тот же ClickHouse, а из PostgreSQL берите только те, которые ещё не попали в него. SAP в Hana именно таким образом и поступает, перенося исторические данные из row store в column store.
И где этот запрос в таком же виде, как предоставил я? Так чтобы это легко можно было повторить любому желающему.
Пример, как оптимизировать на 20-30% выборку 68% их 10 млн. строк Вам не интересен, так как у Вас уникальные пользователи, которым это не нужно.
Пример, как оптимизировать выборку сотни строк из этих же 10 миллионов Вам тоже не интересен.
Ну так дайте пример, который интересен.
А к БД можно обращаться без драйвера? И при чем тут REST? Может еще gRPC и SOAP приплести? )))
Вы действительно думаете, что если есть ORM, то обратится из неё к оптимизированному представлению или функции уже нельзя? Проблема то как раз именно в 1С, которая в отличии от SAP, OEBS, Dynamics и т.п. практически не позволяет создавать представления и пользовательские функции в БД в целях оптимизации. 1С в этом смысле уникальна, скрывая имена таблиц и полей от разработчика и даже не гарантируя, что эти имена вдруг не изменятся при очередной модификации конфигурации.
По крайней мере для поддержки 1С они сделали в разы больше, чем кто-либо другой.
Ещё раз и уже на пальцах.
Для начала, nested loop далеко не единственный метод реализации соединения таблиц. И я начал как раз со сценария, где он крайне неэффективен. А из того, что Вы с такими сценариями не сталкиваетесь, совершенно не следует, что их нет у многих других.
Оборачивая выражение в CASE, как я показал выше, мы добиваемся именно того, что условия зависящие от предыдущих связей (внешней группы) будут вычислены и проверены до условий присоединения текущей связи. И опять таки, как я заметил выше, никто не запрещает создать статистики по выражению. В данном случае - по CASE. Причем пользы от такой статистики будет намного больше, чем от статистики только по item_id.
Во-первых, если в таблице order_lines миллиард строк, то станет только хуже. Во-вторых, в ряде случаев эффективней может оказаться сортировка не по popularity, а по item_id, позволяющая от nested loop перейти к merge join. Даже не смотря на необходимость второй сортировки. Особенно ярко это проявится, если индексы связываемых таблиц ещё на диске, а не в памяти.
Снова приходим к кривизне запроса. Если подзапрос нельзя трансформировать в JOIN, то и о его селективности планировщик ничего не знает. Только разработчик может запинать планировщик в нужный план, имея представление об этой селективности. Хотя куда логичней сразу написать запрос так, чтобы планировщик мог получить информацию о селективности. Иногда это приходится делать ценой заливки данных в промежуточную нежурналируемую таблицу. И тогда планировщик уже сможет понять, какой же механизм соединения тут лучше применить.
Возвращаемся снова к соединению по CASE, который позволяет использовать статистики, если они созданы.
И в качестве вишенки на торт, самое главное. Ни один из перечисленных способов не позволяет планировщику действительно выбрать оптимальный план запроса в моём примере с выборкой десятков строк из миллионов. А вот CASE и создание статистик по ним сразу творят чудеса. Потому что тут уже планировщик знает селективность до вычисления каких-либо фильтров.
Для игрального сервера, которому в сумме надо переживать ~14 миллионов строк - вполне нормально.
Так тут и разницы не увидите в любом случае, так как она окажется в пределах 10 мс.
Это никто не запрещает, вот только это будет медленней на миллионах записях (у меня получилось уже 16 секунд на тех же 6.8 миллионах из заголовка и столько же суммарно из трёх деталей). На малом количестве, в сотню записей, загнав планировщик в nested loop действительно будет на два порядка быстрее, чем в исходном варианте.
Если заранее известно, что записей десятки, то кто запрещает так переписать запрос?
P.S. Я понимаю, что в рамках 1С такое переписывание запроса может оказаться весьма непростой задачей. Я лишь показал, почему проблемы 1С не встречают активного отклика в PostgreSQL community и, очень часто, остаются в пределах Postgres Pro. С точки зрения community - "внесите изменения в платформу, чтобы она более оптимальные запросы строила, и радуйтесь".
Я понимаю, что возможны варианты, но они относятся больше к запросам, обрабатывающим буквально тысячи записей, где проблема решается масштабированием. А как только речь зайдет хотя бы о сотне тысяч, если не миллионах, то разница будет намного больше.
Например, на нескольких миллионах разница набегает уже ~3 секунды или уже свыше 30% провала в производительности.
Чтобы не быть голословным
Execution Time: 8112.906 ms
против
Execution Time: 5180.712 ms
Я бы добавил ещё, что от статистики по полиморфной ссылке по жизни толку мало для планировщика, так как он не знает, на какую же таблицу она ссылается. При этом никто не запрещает создать статистики и по CASE.
А почему бы не поступить так?
У меня подобный подход приводит к ~20% улучшения производительности.