【问题标题】:PostgreSQL: JOIN if a table is referenced more than oncePostgreSQL:如果一个表被多次引用,则加入
【发布时间】:2019-03-09 22:33:04
【问题描述】:

如果一个表在多个列中被另一个表引用,那么从两个表中获取数据的最快方法是什么?

考虑一个包含公司名称的表格和一个包含合同的表格。每个合约都可以有一个client、一个intermediary和一个contractor——在每个组合中。每个值可能是null,同一家公司可能在每个合同行中出现一次、两次或三次。

表定义为:

CREATE TABLE company (id integer,name text);

CREATE TABLE contract (id integer, client integer, intermediary integer, contractor integer);

我用下面的测试数据创建了一个 SQL 小提琴: https://www.db-fiddle.com/f/irCodeZjeEPWvhmRwMcHqT/0

测试数据:

INSERT INTO company (id,name) VAlUES (1,'Company 1');
INSERT INTO company (id,name) VAlUES (2,'Company 2');
INSERT INTO company (id,name) VAlUES (3,'Company 3');
INSERT INTO company (id,name) VAlUES (4,'Company 4');
INSERT INTO company (id,name) VAlUES (5,'Company 5');
INSERT INTO contract (id,client,intermediary,contractor) VAlUES (1,NULL,NULL,NULL);
INSERT INTO contract (id,client,intermediary,contractor) VAlUES (2,NULL,2,3);
INSERT INTO contract (id,client,intermediary,contractor) VAlUES (3,1,NULL,NULL);
INSERT INTO contract (id,client,intermediary,contractor) VAlUES (4,NULL,2,NULL);
INSERT INTO contract (id,client,intermediary,contractor) VAlUES (5,1,2,3);
INSERT INTO contract (id,client,intermediary,contractor) VAlUES (6,4,NULL,5);
INSERT INTO contract (id,client,intermediary,contractor) VAlUES (7,1,NULL,1);
INSERT INTO contract (id,client,intermediary,contractor) VAlUES (7,3,3,3);

现在,使用 PostgreSQL 9.6,需要一个查询来返回合同 ID 和每个相关公司的名称。使用子查询很容易:

SELECT
id, 
(SELECT name FROM company WHERE id = client) AS "clientName",
(SELECT name FROM company WHERE id = intermediary) AS "intermediaryName",
(SELECT name FROM company WHERE id = contractor) AS "contractorName"
FROM contract;

但是,在现实世界中,由于查询复杂得多,我们在这里遇到了性能问题。现在的问题是:有没有办法改进它? JOIN 会比子查询快吗?如果是:那将如何运作?

当然,你可以做类似的事情

SELECT * FROM contract LEFT JOIN company ON company.id = ANY(ARRAY[client,contractor,intermediary]);,

但在这种情况下,哪个公司在合同中扮演哪个角色的信息会丢失。

(编辑:在现实世界中,有索引、外键约束和其他东西。为了简洁起见,我把所有这些都放在一边了。)

【问题讨论】:

  • 我目前的一般评论是“更好”/“最好”等:除非定义它,否则工程中没有“更好”/“最好”之类的东西。同样不幸的是,所有合理的实际定义都需要大量的经验,以及与对细节的混乱敏感度相互作用的大量因素。进行简单的设计。当您通过测量证明您可以想到的设计和所有替代方案都存在问题时(无论当时意味着什么),然后提出一个非常具体的问题。这也应该定义“更好”/“最好”。 meta.stackexchange.com/q/204461

标签: sql postgresql performance join subquery


【解决方案1】:

你的方法很好,虽然你应该使用表别名:

SELECT id, 
       (SELECT c.name FROM company c WHERE c.id = co.client) AS "clientName",
       (SELECT c.name FROM company c WHERE c.id = co.intermediary) AS "intermediaryName",
       (SELECT c.name FROM company c WHERE c.id = co.contractor) AS "contractorName"
FROM contract co;

id 应该是company 中的主键——或者在其上建立索引。

您也可以使用left join 来表达这一点:

SELECT id, cc.name as clientName, ci.name as intermediaryName, cco.name as contractorName
FROM contract co LEFT JOIN
     company cc
     ON c.id = co.client LEFT JOIN
     company ci
     ON ci.id = co.intermediary LEFT JOIN
     company cco
     ON cco.id = co.contractor;

这两种方法的性能应该非常相似。

【讨论】:

    【解决方案2】:

    我觉得 Gordon 的解决方案很好(尤其是第二个,带有外部连接)。

    您是否在表 Contract 中的 Client、Intermediary 和 Contractor 列上添加了外键和索引?

    【讨论】:

    • 是的,有索引和外键。把它放在一边。
    猜你喜欢
    • 1970-01-01
    • 2016-07-28
    • 2012-05-30
    • 1970-01-01
    • 2015-06-12
    • 1970-01-01
    • 2019-05-17
    • 1970-01-01
    • 2018-01-08
    相关资源
    最近更新 更多