Поддерживает ли PostgreSQL заказ CTE?
В PostgreSQL общие табличные выражения (CTE) - это оптимизационные заграждения. Это означает, что CTE материализуется в память и что предикаты из другого запроса никогда не будут вдавлены в CTE.
Теперь мне интересно, будут ли другие метаданные об CTE, такие как упорядочение, разделяться на другие запросы. Возьмем следующий запрос:
WITH ordered_objects AS
(
SELECT * FROM object ORDER BY type ASC LIMIT 10
)
SELECT MIN(type) FROM ordered_objects
Здесь MIN(type)
, очевидно, всегда является первой строкой ordered_objects
(или NULL
если ordered_objects
пусты), поскольку ordered_objects
уже упорядочены по type
. Доступны ли эти знания о ordered_objects
при оценке SELECT MIN(type) FROM ordered_objects
?
Ответы
Ответ 1
Если я правильно понимаю ваш вопрос - нет, это не так. нет таких знаний. Как вы найдете в примере ниже. когда вы ограничиваете до 10 строк, выполнение выполняется очень быстро - меньше данных для обработки (в моем случае в миллион раз меньше), что означало бы, что CTE сканирует весь упорядоченный набор, игнорируя факт, что min
будет в первых строках...
данные:
t=# create table object (type bigint);
CREATE TABLE
Time: 4.636 ms
t=# insert into object select generate_series(1,9999999);
INSERT 0 9999999
Time: 7769.275 ms
с пределом:
explain analyze WITH ordered_objects AS
(
SELECT * FROM object ORDER BY type ASC LIMIT 10
)
SELECT MIN(type) FROM ordered_objects;
Время выполнения: 3150,183 мс
https://explain.depesz.com/s/5yXe
без:
explain analyze WITH ordered_objects AS
(
SELECT * FROM object ORDER BY type ASC
)
SELECT MIN(type) FROM ordered_objects;
Время выполнения: 16032,989 мс
https://explain.depesz.com/s/1SU
Я, разумеется, разогревал данные перед испытаниями
Ответ 2
- [в Postgres] CTE всегда вызывается один раз
- , даже если он ссылался более одного раза
- его результат хранится во временную таблицу (материализованную)
- внешний запрос не знает о внутренней структуре (индексы недоступны) или порядок (не уверен в частотных оценках), он просто сканирует временные результаты
- в приведенном ниже фрагменте CTE сканируется дважды, даже если результаты, как известно, идентичны.
\d react
EXPLAIN ANALYZE
WITH omg AS (
SELECT topic_id
, row_number() OVER (PARTITION by krant_id ORDER BY topic_id) AS rn
FROM react
WHERE krant_id = 1
AND topic_id < 5000000
ORDER BY topic_id ASC
)
SELECT MIN (o2.topic_id)
FROM omg o1 --
JOIN omg o2 ON o1.rn = o2.rn -- exactly the same
WHERE o1.rn = 1
;
Table "public.react"
Column | Type | Modifiers
------------+--------------------------+--------------------
krant_id | integer | not null default 1
topic_id | integer | not null
react_id | integer | not null
react_date | timestamp with time zone |
react_nick | character varying(1000) |
react_body | character varying(4000) |
zoek | tsvector |
Indexes:
"react_pkey" PRIMARY KEY, btree (krant_id, topic_id, react_id)
"react_krant_id_react_nick_react_date_topic_id_react_id_idx" UNIQUE, btree (krant_id, react_nick, react_date, topic_id, react_id)
"react_date" btree (krant_id, topic_id, react_date)
"react_nick" btree (krant_id, topic_id, react_nick)
"react_zoek" gin (zoek)
Triggers:
tr_upd_zzoek_i BEFORE INSERT ON react FOR EACH ROW EXECUTE PROCEDURE tf_upd_zzoek()
tr_upd_zzoek_u BEFORE UPDATE ON react FOR EACH ROW WHEN (new.react_body::text <> old.react_body::text) EXECUTE PROCEDURE tf_upd_zzoek()
----------
Aggregate (cost=232824.29..232824.29 rows=1 width=4) (actual time=1773.643..1773.645 rows=1 loops=1)
CTE omg
-> WindowAgg (cost=0.43..123557.17 rows=402521 width=8) (actual time=0.217..1246.577 rows=230822 loops=1)
-> Index Only Scan using react_pkey on react (cost=0.43..117519.35 rows=402521 width=8) (actual time=0.161..419.916 rows=230822 loops=1)
Index Cond: ((krant_id = 1) AND (topic_id < 5000000))
Heap Fetches: 442
-> Nested Loop (cost=0.00..99136.69 rows=4052169 width=4) (actual time=0.264..1773.624 rows=1 loops=1)
-> CTE Scan on omg o1 (cost=0.00..9056.72 rows=2013 width=8) (actual time=0.249..59.252 rows=1 loops=1)
Filter: (rn = 1)
Rows Removed by Filter: 230821
-> CTE Scan on omg o2 (cost=0.00..9056.72 rows=2013 width=12) (actual time=0.003..1714.355 rows=1 loops=1)
Filter: (rn = 1)
Rows Removed by Filter: 230821
Total runtime: 1782.887 ms
(14 rows)