PostgreSQL Оптимальный SELECT-запрос со многими таблицами - PullRequest
0 голосов
/ 03 декабря 2018

Я использую базу данных PostgreSQL и имею 4 таблицы для работы: реестр, счет-фактура, invoice_position, user_involved

Столбцы реестра таблицы:

id
reg_name

Счет-фактура таблицы:

id
reg_id(FK to registry, can be many invoices in one registry)
invoice_name

Таблица invoice_position:

id
invoice_id(FK to invoice, can be many positions in invoice)
position_name

Таблица user_involved:

id
Inv_position_id(FK to invoice_position, it's a one to one relationship)
user_name

Мне нужно создать запрос SELECT, который найдет, если существует более одного вхожденияиз user_involved в таблице registry (которая связана только с user_involved FK из invoice_position-> invoice-> registry).Несколько вещей, на которые стоит обратить внимание, что эти таблицы содержат более +/- 1.000.000 строк в каждой (за исключением таблицы реестра), и я не могу сейчас добавить прямую связь между реестром и user_involved.Вопрос в том, как написать оптимальный SELECT или, если это вообще возможно, в этом состоянии, потому что вложение нескольких SELECT будет длиться вечно.

РЕДАКТИРОВАТЬ: Это то, что я пробовал до сих пор:

SELECT rg.id, COUNT(ui.id) FROM registry rg
  LEFT JOIN invoice inv ON inv.reg_id = rg.id
  LEFT JOIN invoice_position ip ON ip.invoice_id = inv.id
  LEFT JOIN user_involved ui ON ui.inv_position_id = ip.id
WHERE ip.id = $id GROUP BY rg.id,ui.id HAVING COUNT (ui.id) > 1;

Этот запрос будет внутри цикла , в то время как с переменной "id", учитывая разные идентификаторы позиции счета.

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Я бы начал с:

EXPLAIN 
SELECT rg.id, COUNT(ui.id)
FROM invoice_position ip, invoice inv, registry rg, user_involved ui
WHERE ip.id = $id
  AND ip.invoice_id = inv.id
  AND inv.reg_id = rg.id
  AND ui.inv_position_id = ip.id
GROUP BY rg.id
HAVING COUNT(ui.id) > 1;

и посмотрел, что ты получишь.причина, по которой вы не используете LEFT OUTER JOIN s, как вы делали, заключается в том, что они ограничивают планы, которые может выбирать оптимизатор (или, по крайней мере, они делали это в прошлый раз, когда я проверял), и этот запрос, похоже, не нуждается в них.

Кроме того, вы можете подумать о том, чтобы получать несколько invoice_position строк одновременно и сопоставлять их вне базы данных.таким образом, вы можете немного распределить затраты на выполнение запроса, компромисс будет немного сложнее в вызывающем коде.

например, что-то вроде:

SELECT ip.id, rg.id, COUNT(ui.id)
FROM invoice_position ip, invoice inv, registry rg, user_involved ui
WHERE ip.id IN $id_list
  AND ip.invoice_id = inv.id
  AND inv.reg_id = rg.id
  AND ui.inv_position_id = ip.id
GROUP BY ip.id, rg.id
HAVING COUNT(ui.id) > 1;
0 голосов
/ 03 декабря 2018

Одним из ключевых аспектов вашего запроса является наличие условия фильтрации.В отсутствие этого нет другого выбора, кроме как читать целые таблицы.Вы не хотите этого делать, конечно.

Альтернатива, о которой всегда стоит упоминать, - создавать / использовать / поддерживать материализованные представления.Но это другая тема, которую я здесь не освещаю, поскольку она требует гораздо больше работы.Я полагаю, что этот вариант - вариант последней инстанции, я полагаю.

В любом случае, ваш запрос имеет условие фильтрации, хотя и только одно, что это равенство (это здорово).

PostgreSQL может воспользоваться этим фильтром, если:

  • Селективность фильтра хорошая.То есть условие ip.id = $i выбирает 5% или менее строк таблицы.В идеале я бы поставил этот предел на уровне «менее 0,5%», чтобы сделать его действительно быстрым.
  • В столбце используется индекс для фильтрации.То есть таблица имеет индекс, который начинается со столбца id.Может быть, индекс только с этим столбцом.Например:

    create index ix1 on invoice_position (id);
    

Если вы подтвердите, что эти условия выполнены, опубликуйте план выполнения вашего запроса.Вы можете сделать это, добавив к вашему запросу пункт EXPLAIN.На самом деле он не выполнит запрос, а просто отобразит план, который вам нужно опубликовать в вашем вопросе.Я могу прочитать его и дать вам мою оценку.

Кстати, какова селективность вашего условия фильтрации?

...