将列值映射到 table 名称并加入

Map column value to table name and join

我有一个看起来像

的复合类型
CREATE TYPE member AS (
    id BIGINT,
    type CHAR(1)
);

我有一个 table 依赖于这个 member 类型的数组。

CREATE TABLE relation (
    id BIGINT PRIMARY KEY,
    members member[]
);

我还有其他三个 table,每个都有不同的架构(但有共同的 id 字段)

CREATE TABLE table_x (
    id BIGINT PRIMARY KEY,
    some_text TEXT
);

CREATE TABLE table_y (
    id BIGINT PRIMARY KEY,
    some_int INT
);

CREATE TABLE table_z (
    id BIGINT PRIMARY KEY,
    some_date TIMESTAMP
);

type字段在member类型中只是一个字符来找出特定成员所属的table。 relation table 中的一行可以混合使用不同的 type

我有一个场景需要返回 relation ids,其中至少有一个 member 满足基于它的类型的特定条件(假设 x => some_text 不为空或 y => some_int 大于 10 或 z => some_date 是从现在开始的一周)。

我可以通过向数据库发出多个请求来在应用程序端实现这个场景:

我想知道是否有办法将列值映射到 table 名称并连接它们。

假设

  • 我假设 relation.members 数组没有超过一个相同类型的成员元素。正确吗?

查询尝试

with unnested_members as (
-- Unnest members array
select id, unnest(members) members
from relation
)
, members_joined as (
-- left join on a per type basis with table_x, table_y and table_z.
select r.id, (r.members).id idext, (r.members).type,
       x.some_text, y.some_int, z.some_date -- more types, more columns here
from unnested_members r
  left join table_x x on (x.id = (r.members).id and (r.members).type = 'x')
  left join table_y y on (y.id = (r.members).id and (r.members).type = 'y')
  left join table_z z on (z.id = (r.members).id and (r.members).type = 'z')
  -- More types, more tables to left join
)
select id,
  max(some_text) some_text, -- use max() to get not null value for this id
  max(some_int) some_int, -- use max() to get not null value for this id
  max(some_date) some_date -- use max() to get not null value for this id
  -- more types, more max() columns here
from members_joined
group by id -- get one row per relation.id with data from joined table_* columns

如果您需要包含更多表格,则必须将这些表格包含在 left join 部分中,将列包含在 select 列表和 max() 部分中.

@JNevill 关于这个数据库设计有一个很好的观点。虽然这种方法可能看起来不是最优的,但它使 table 定义清楚地分开,它们之间没有任何关系。此外 relation table 的大小与其他三个 table 相比相当小。

我通过简单地获取每种类型的行并合并它们解决了这个问题:

SELECT relation.* FROM relation, UNNEST(relation.members) member INNER JOIN table_x ON member.id = table_x.id WHERE member.type = 'x' AND table_x.some_text = 'some text value' 
UNION 
SELECT relation.* FROM relation, UNNEST(relation.members) member INNER JOIN table_y ON member.id = table_y.id WHERE member.type = 'y' AND table_y.some_int = 123 
UNION 
SELECT relation.* FROM relation, UNNEST(relation.members) member INNER JOIN table_z ON member.id = table_z.id WHERE member.type = 'z' AND table_z.some_date > '2017-01-11 00:00:00';