具有许多表的PostgreSQL最优SELECT查询

时间:2018-12-03 12:21:36

标签: sql postgresql join subquery query-optimization

我正在使用PostgreSQL数据库,并且有4个表可以使用:注册表,发票,发票位置,user_involved

表注册表列:

id
reg_name

表格发票:

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

表格发票位置:

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查询,如果在 registry 表中出现了多次 user_involved (仅与user_involved与FK的invoice_position相关,则查找) ->发票->注册)。没什么要注意的是,这些表每个表都包含超过+/- 1.000.000行(EXCEPT注册表表),我目前不能在注册表和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;

此查询将在 while 循环内,其中变量“ id”具有不同的发票位置ID。

2 个答案:

答案 0 :(得分:0)

查询的关键方面之一是存在过滤条件。如果没有它,除了读取整个表,别无选择。当然,您不想这样做。

始终值得一提的替代方法是创建/使用/维护实例化视图。但这是我不在此讨论的主题,因为它需要做很多工作。我想,我认为这是不得已的选择。

无论如何,您的查询确实有一个过滤条件,尽管只有一个,这是一个相等条件(这很棒)。

在以下情况下,PostgreSQL可以充分利用此过滤器:

  • 过滤器的选择性很好。即,条件ip.id = $i选择表行的5%或更少。理想情况下,我会将此限制设置为“小于0.5%”以使其真正快速。
  • 该列上有一个用于过滤的索引。也就是说,该表的索引以列id开头。也许只有该列的索引。例如:

    create index ix1 on invoice_position (id);
    

如果您确认满足这些条件,请发布查询的执行计划。您可以通过在查询中添加EXPLAIN子句来实现。它实际上不会执行查询,而只会显示您需要在问题中发布的计划。我可以阅读,并给我我的评估。

顺便问一下,您的过滤条件的选择性是什么?

答案 1 :(得分:0)

我将从做起:

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,是因为它们限制了优化器可以选择的计划(或者至少是我上次检查时选择的计划),并且此查询似乎不需要它们。 / p>

此外,您可以考虑一次获取几行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;