PostgreSQL: JOIN, если на таблицу ссылаются более одного раза - PullRequest
0 голосов
/ 04 октября 2018

Какой самый быстрый способ извлечения данных из двух таблиц, если на одну таблицу ссылаются из другой в нескольких столбцах?

Рассмотрим таблицу с названиями компаний и таблицу с контрактами.Каждый контракт может иметь client, intermediary и contractor - в каждой комбинации.Каждое значение может быть null, а одна и та же компания может быть один, два или три раза в каждой строке контракта.

Определения таблицы:

CREATE TABLE company (id integer,name text);

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

Я создал скрипту SQL с тестом da ниже: 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, необходим запрос, который возвращает идентификатор контракта с названием каждой участвующей компании.Довольно просто с подзапросами:

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]);,

, но в этом случае информация, которую компания играет, какаяроль в контракте теряется.

(Правка: В реальном мире существуют индексы, ограничения внешнего ключа и прочее. Я оставил все это здесь для краткости.)

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

Решение Гордона мне подходит (особенно второе, с внешними объединениями).

Вы добавили внешние ключи и индексы в столбцы Клиент, Посредник и Подрядчик в таблице Контракт?

0 голосов
/ 04 октября 2018

Ваш метод подходит, хотя вы должны использовать псевдонимы таблиц:

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;

Производительность должна быть примерно одинаковой между двумя методами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...