Postgresql 14.
嗨!我有多个表,它们的名称类似于model_<uuid>
,每个表的列的名称类似于element_<uuid>
。所有这些表都在models
模式中。我有一个查询,转换information_schema.columns
视图在类似m2m表的东西。
SELECT
SPLIT_PART(table_name, '_', 2)::UUID AS model_id,
SPLIT_PART(column_name, '_', 2)::UUID AS element_id
FROM information_schema.columns
WHERE
table_schema = 'models' AND
table_name LIKE 'model\_%' AND
column_name LIKE 'element\_%'
它工作正常。但是当我试图将其 Package 在视图中或将其用作子查询时,我得到了以下错误。
子查询示例:
SELECT model_id, ARRAY_AGG(element_id) AS element_ids
FROM (
SELECT
SPLIT_PART(table_name, '_', 2)::UUID AS model_id,
SPLIT_PART(column_name, '_', 2)::UUID AS element_id
FROM information_schema.columns
WHERE
table_schema = 'models' AND
table_name LIKE 'model\_%' AND
column_name LIKE 'element\_%'
) AS t
WHERE model_id = '2ba221e1-f2e3-4438-bafb-8e7bfefe161f'
GROUP BY model_id
错误:
ERROR: invalid input syntax for type uuid: "elements"
我有表elements
,但它在public
模式中,我有table_schema = 'models'
过滤器。我认为这个过滤器不起作用,子查询试图将其转换为“uuid”。但是当我以普通(root?)查询时,所有行都取自models
模式。
当我删除WHERE model_id = '2ba221e1-f2e3-4438-bafb-8e7bfefe161f'
时,它又正常工作了。
怎么了?简直让我大吃一惊。
UPD:
好吧。我明白原因。错误的原因是查询优化器将查询筛选器model_id = '...'
移动到子查询筛选器内部。在public.model_elements
视图上引发错误。
我从查询中删除table_name
中的所有UUID并运行以下查询:
EXPLAIN SELECT model_id, ARRAY_AGG(element_id)
FROM (
SELECT
table_name AS model_id,
SPLIT_PART(column_name, '_', 2)::UUID AS element_id
FROM information_schema.columns
WHERE
table_schema = 'models'
AND table_name LIKE 'model\_%'
AND column_name LIKE 'element\_%'
) AS t
WHERE model_id = 'abc'
GROUP BY model_id;
查询计划的片段:
-> Index Scan using pg_class_relname_nsp_index on pg_class c (cost=0.28..4.31 rows=1 width=76)
Index Cond: ((relname >= 'model_'::text) AND (relname < 'model`'::text) AND (relname = 'abc'::name))
Filter: ((relname ~~ 'model\_%'::text) AND (relkind = ANY ('{r,v,f,p}'::""char""[])))"
-> Index Scan using pg_attribute_relid_attnam_index on pg_attribute a (cost=0.29..4.32 rows=1 width=78)
Index Cond: ((attrelid = c.oid) AND (attname >= 'element_'::text) AND (attname < 'element`'::text))
Filter: ((NOT attisdropped) AND (attnum > 0) AND (attname ~~ 'element\_%'::text) AND (pg_has_role(c.relowner, 'USAGE'::text) OR has_column_privilege(c.oid, attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text)))"
如您所见,它通过table_name,
组合了所有过滤器(根过滤器和子查询过滤器),包括SPLIT_PART(table_name, '_', 2)::UUID
转换(如果我们考虑原始查询),并首先使用它们。也就是说,它首先尝试将所有表名转换为UUID,忽略那些过滤器过滤掉无法完成的表。
我发现了问题,但如何解决它?
1条答案
按热度按时间zdwk9cvp1#
PostgreSQL必须先计算
SPLIT_PART(table_name, '_', 2)::UUID
,然后才能与'2ba221e1-f2e3-4438-bafb-8e7bfefe161f'
进行比较?只需避免转换为uuid
并比较字符串。此外,PostgreSQL可以自由地重新排列查询,只要它保持语义等价。在当前的例子中,它会拉取子查询并首先计算索引条件。如果您想阻止PostgreSQL拉取子查询,请在子查询的末尾添加
OFFSET 0
。这在语义上没有区别,但却是优化器的障碍。