使用 OR 语句在多个可能的列上加入 Postgres
Postgres JOIN on multiple possible columns with OR statement
我有两个 table 想要连接在一起:
contracts
:
id
customer_id_1
customer_id_2
customer_id_3
date
1
MAIN1
TRAN1
TRAN2
20201101
2
MAIN2
20201001
3
MAIN3
TRAN5
20200901
4
MAIN4
TRAN7
TRAN8
20200801
customers
:
id
customer_id
info
date
1
MAIN1
blah
20200930
2
TRAN2
blah
20200929
3
TRAN5
blah
20200831
4
TRAN7
blah
20200801
在我的 contracts
table 中,每一行代表与客户的合同,他们可能有 1 个或多个不同的 ID,他们在 customers
[=50] 中被引用=].在 customers
table 中,我有关于客户的信息(可以是每个客户不同日期的零条或多条记录)。我想执行从 contracts
到 customers
的连接,以便在记录合同时获得有关客户的最新可用信息,而忽略合同之后可能可用的任何潜在客户信息日期。我对没有客户信息的合同也不感兴趣。这里的主要问题是,在 customers
中,每个客户记录都可以引用可能存在的 3 个 ID 中的任意一个。
我目前有以下查询按预期执行任务但问题是当 运行 在 50-100k 行范围内的数据时 非常 慢.如果我删除 INNER JOIN
中的 OR
语句并仅加入第一个 ID,查询将在几秒钟内执行,而不是大约半小时。
SELECT
DISTINCT ON (ctr.id)
ctr.id,
ctr.customer_id_1,
ctr.date AS contract_date,
cst.info,
cst.date AS info_date
FROM
contracts ctr
INNER JOIN customers cst ON (
cst.customer_id = ctr.customer_id_1
OR cst.customer_id = ctr.customer_id_2
OR cst.customer_id = ctr.customer_id_3
)
AND ctr.date >= cst.date
ORDER BY
ctr.id,
cst.date DESC
结果:
id
customer_id_1
contract_date
info
info_date
1
MAIN1
20201101
blah
20200930
3
MAIN3
20200901
blah
20200831
4
MAIN4
20200801
blah
20200801
似乎 JOIN
中的 OR
语句不是很常见(我几乎没有在网上找到任何示例),我认为这是因为必须有更好的方法这个。所以我的问题是,如何优化它?
OR
通常是 SQL 谓词中的性能杀手。
加入前的一个替代逆轴:
select distinct on (ctr.id)
ctr.id,
ctr.customer_id_1,
ctr.date as contract_date,
cst.info,
cst.date as info_date
from contracts ctr
cross join lateral (values
(ctr.customer_id_1), (ctr.customer_id_2), (ctr.customer_id_3)
) as ctx(customer_id)
inner join customers cst on cst.customer_id = ctx.customer_id and ctr.date >= cst.date
order by ctr.id, cst.date desc
这种技术的使用表明您可以极大地改进您的数据模型:合同和客户之间的关系应该存储在单独的 table 中,每个 customer/contract 元组在单独的行中- 本质上,查询所做的实际上是在横向连接中构建派生的 table。
我有两个 table 想要连接在一起:
contracts
:
id | customer_id_1 | customer_id_2 | customer_id_3 | date |
---|---|---|---|---|
1 | MAIN1 | TRAN1 | TRAN2 | 20201101 |
2 | MAIN2 | 20201001 | ||
3 | MAIN3 | TRAN5 | 20200901 | |
4 | MAIN4 | TRAN7 | TRAN8 | 20200801 |
customers
:
id | customer_id | info | date |
---|---|---|---|
1 | MAIN1 | blah | 20200930 |
2 | TRAN2 | blah | 20200929 |
3 | TRAN5 | blah | 20200831 |
4 | TRAN7 | blah | 20200801 |
在我的 contracts
table 中,每一行代表与客户的合同,他们可能有 1 个或多个不同的 ID,他们在 customers
[=50] 中被引用=].在 customers
table 中,我有关于客户的信息(可以是每个客户不同日期的零条或多条记录)。我想执行从 contracts
到 customers
的连接,以便在记录合同时获得有关客户的最新可用信息,而忽略合同之后可能可用的任何潜在客户信息日期。我对没有客户信息的合同也不感兴趣。这里的主要问题是,在 customers
中,每个客户记录都可以引用可能存在的 3 个 ID 中的任意一个。
我目前有以下查询按预期执行任务但问题是当 运行 在 50-100k 行范围内的数据时 非常 慢.如果我删除 INNER JOIN
中的 OR
语句并仅加入第一个 ID,查询将在几秒钟内执行,而不是大约半小时。
SELECT
DISTINCT ON (ctr.id)
ctr.id,
ctr.customer_id_1,
ctr.date AS contract_date,
cst.info,
cst.date AS info_date
FROM
contracts ctr
INNER JOIN customers cst ON (
cst.customer_id = ctr.customer_id_1
OR cst.customer_id = ctr.customer_id_2
OR cst.customer_id = ctr.customer_id_3
)
AND ctr.date >= cst.date
ORDER BY
ctr.id,
cst.date DESC
结果:
id | customer_id_1 | contract_date | info | info_date |
---|---|---|---|---|
1 | MAIN1 | 20201101 | blah | 20200930 |
3 | MAIN3 | 20200901 | blah | 20200831 |
4 | MAIN4 | 20200801 | blah | 20200801 |
似乎 JOIN
中的 OR
语句不是很常见(我几乎没有在网上找到任何示例),我认为这是因为必须有更好的方法这个。所以我的问题是,如何优化它?
OR
通常是 SQL 谓词中的性能杀手。
加入前的一个替代逆轴:
select distinct on (ctr.id)
ctr.id,
ctr.customer_id_1,
ctr.date as contract_date,
cst.info,
cst.date as info_date
from contracts ctr
cross join lateral (values
(ctr.customer_id_1), (ctr.customer_id_2), (ctr.customer_id_3)
) as ctx(customer_id)
inner join customers cst on cst.customer_id = ctx.customer_id and ctr.date >= cst.date
order by ctr.id, cst.date desc
这种技术的使用表明您可以极大地改进您的数据模型:合同和客户之间的关系应该存储在单独的 table 中,每个 customer/contract 元组在单独的行中- 本质上,查询所做的实际上是在横向连接中构建派生的 table。