使用 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 中,我有关于客户的信息(可以是每个客户不同日期的零条或多条记录)。我想执行从 contractscustomers 的连接,以便在记录合同时获得有关客户的最新可用信息,而忽略合同之后可能可用的任何潜在客户信息日期。我对没有客户信息的合同也不感兴趣。这里的主要问题是,在 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。