为啥递归联合不适用于 PostgreSQL 中的复合类型

Posted

技术标签:

【中文标题】为啥递归联合不适用于 PostgreSQL 中的复合类型【英文标题】:Why recursive union does not work with composite types in PostgreSQL为什么递归联合不适用于 PostgreSQL 中的复合类型 【发布时间】:2018-06-17 13:13:02 【问题描述】:

我有一个包含复合类型字段的表。当我尝试对此类字段执行递归联合时,出现错误。

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;

【问题讨论】:

【参考方案1】:

有a thread on PostgreSQL hackers mailing list 和 Tom Lane 的简短解释:

一般来说,我们认为数据类型的相等概念可以通过其默认的 btree opclass(支持基于排序的查询算法)或默认的哈希 opclass(支持基于哈希的查询算法)来定义。

普通的 UNION 代码支持排序或散列,但我们还没有支持基于排序的递归 UNION 方法。我不相信这是值得做的......

作为一种解决方法,使用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

【讨论】:

以上是关于为啥递归联合不适用于 PostgreSQL 中的复合类型的主要内容,如果未能解决你的问题,请参考以下文章

F#如何在递归可区分联合中指定类型限制

为啥条件渲染不适用于 vuejs 中的表单输入

为啥虚拟键盘不适用于 Qt 中的 QDialog 文本框?

为啥我在 postgresql 中的视图不使用索引?

为啥相同的语句不适用于 C++ 中的类成员?

为啥 Width="*" 不适用于位于 RowDetailsTemplate 中的 DataGrid 中的列