将列值映射到 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
是从现在开始的一周)。
我可以通过向数据库发出多个请求来在应用程序端实现这个场景:
- unnest
relation
table
- 每个
relation
收集 member
数据
- 发出新请求以找出
relation
s
我想知道是否有办法将列值映射到 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';
我有一个看起来像
的复合类型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
是从现在开始的一周)。
我可以通过向数据库发出多个请求来在应用程序端实现这个场景:
- unnest
relation
table - 每个
relation
收集 - 发出新请求以找出
relation
s
member
数据
我想知道是否有办法将列值映射到 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';