为什么递归联合不适用于 PostgreSQL 中的复合类型
Why recursive union does not work with composite types in PostgreSQL
我有一个包含复合类型字段的 table。当我尝试对此类字段执行递归联合时,出现错误。
drop type example_t cascade;
create type example_t as (
value text,
key text
);
drop table if exists example cascade;
create table example (
inbound example_t,
outbound example_t,
primary key (inbound, outbound)
);
create or replace function example_fn(_attrs example_t[])
returns table (attr example_t) as $$
with recursive target as (
select outbound
from example
where array[inbound] <@ _attrs
union
select r.outbound
from target as t
inner join example as r on r.inbound = t.outbound
)
select unnest(_attrs)
union
select * from target;
$$ language sql immutable;
select example_fn(array[('foo', 'bar') ::example_t]);
ERROR: could not implement recursive UNION DETAIL: All column datatypes must be hashable. CONTEXT: SQL function "example_fn" during startup SQL state: 0A000
非递归联合有效
create or replace function example_fn(_attrs example_t[])
returns table (attr example_t) as $$
select unnest(_attrs)
union
select * from example;
$$ language sql immutable;
select example_fn(array[('foo', 'bar') ::example_t]);
我可以通过这种方式重构我的函数以使其工作。但它看起来很奇怪。我的意思是它的可读性较差。有什么办法可以做得更好吗?
create or replace function example_fn(_attrs example_t[])
returns table (attr example_t) as $$
with recursive target as (
select (outbound).value, (outbound).key
from example
where array[inbound] <@ _attrs
union
select (r.outbound).value, (r.outbound).key
from target as t
inner join example as r on r.inbound = (t.value, t.key) ::example_t
)
select (unnest(_attrs)).*
union
select * from target;
$$ language sql immutable;
有 a thread on PostgreSQL hackers mailing list 和 Tom Lane 的简短解释:
In general we consider that a datatype's notion of equality can be defined either by its default btree opclass (which supports sort-based query algorithms) or by its default hash opclass (which supports hash-based query algorithms).
The plain UNION code supports either sorting or hashing, but we've not gotten around to supporting a sort-based approach to recursive UNION. I'm not convinced that it's worth doing ...
解决方法是使用 union all
:
with recursive target as (
select outbound
from example
where inbound = ('a', 'a')::example_t
union all
select r.outbound
from target as t
inner join example as r on r.inbound = t.outbound
)
select *
-- or, if necessary
-- select distinct *
from target
我有一个包含复合类型字段的 table。当我尝试对此类字段执行递归联合时,出现错误。
drop type example_t cascade;
create type example_t as (
value text,
key text
);
drop table if exists example cascade;
create table example (
inbound example_t,
outbound example_t,
primary key (inbound, outbound)
);
create or replace function example_fn(_attrs example_t[])
returns table (attr example_t) as $$
with recursive target as (
select outbound
from example
where array[inbound] <@ _attrs
union
select r.outbound
from target as t
inner join example as r on r.inbound = t.outbound
)
select unnest(_attrs)
union
select * from target;
$$ language sql immutable;
select example_fn(array[('foo', 'bar') ::example_t]);
ERROR: could not implement recursive UNION DETAIL: All column datatypes must be hashable. CONTEXT: SQL function "example_fn" during startup SQL state: 0A000
非递归联合有效
create or replace function example_fn(_attrs example_t[])
returns table (attr example_t) as $$
select unnest(_attrs)
union
select * from example;
$$ language sql immutable;
select example_fn(array[('foo', 'bar') ::example_t]);
我可以通过这种方式重构我的函数以使其工作。但它看起来很奇怪。我的意思是它的可读性较差。有什么办法可以做得更好吗?
create or replace function example_fn(_attrs example_t[])
returns table (attr example_t) as $$
with recursive target as (
select (outbound).value, (outbound).key
from example
where array[inbound] <@ _attrs
union
select (r.outbound).value, (r.outbound).key
from target as t
inner join example as r on r.inbound = (t.value, t.key) ::example_t
)
select (unnest(_attrs)).*
union
select * from target;
$$ language sql immutable;
有 a thread on PostgreSQL hackers mailing list 和 Tom Lane 的简短解释:
In general we consider that a datatype's notion of equality can be defined either by its default btree opclass (which supports sort-based query algorithms) or by its default hash opclass (which supports hash-based query algorithms).
The plain UNION code supports either sorting or hashing, but we've not gotten around to supporting a sort-based approach to recursive UNION. I'm not convinced that it's worth doing ...
解决方法是使用 union all
:
with recursive target as (
select outbound
from example
where inbound = ('a', 'a')::example_t
union all
select r.outbound
from target as t
inner join example as r on r.inbound = t.outbound
)
select *
-- or, if necessary
-- select distinct *
from target