索引以查找不存在外键的记录

sam*_*mol 5 sql postgresql indexing materialized-views postgresql-performance

table products
id primary_key

table transactions
product_id foreign_key references products
Run Code Online (Sandbox Code Playgroud)

下面的SQL查询非常慢:

SELECT products.* 
FROM   products 
       LEFT JOIN transactions 
              ON ( products.id = transactions.product_id ) 
WHERE  transactions.product_id IS NULL; 
Run Code Online (Sandbox Code Playgroud)

在1亿个产品记录中,可能只有100条记录中没有相应交易的产品。

该查询非常慢,因为我怀疑它正在进行全表扫描以查找那些空外键产品记录。

我想创建这样的部分索引:

CREATE INDEX products_with_no_transactions_index 
ON (Left JOIN TABLE 
    BETWEEN products AND transactions) 
WHERE transactions.product_id IS NULL;
Run Code Online (Sandbox Code Playgroud)

以上可能吗,我将如何处理?

注意:此数据集的一些特征:

  1. 交易永远不会被删除,只会被添加。

  2. 产品永远不会被删除,而是以每分钟100s的速度添加(显然,这是一个复杂得多的实际用例背后的虚构示例)。其中的一小部分是暂时孤立的

  3. 我需要经常查询(每分钟最多一次),并且需要始终知道当前的一组孤立产品是什么

Erw*_*ter 5

我能想到的最好的是你在评论中的最后一个想法:物化视图

CREATE MATERIALIZED VIEW orphaned_products AS
SELECT *
FROM   products p
WHERE  NOT EXISTS (SELECT 1 FROM transactions t WHERE t.product_id = p.id)
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用此表(物化视图只是一个表)作为products处理孤立产品的查询中大表的替代品 - 显然对性能有很大影响(只有 100 行而不是 1 亿行)。物化视图需要Postgres 9.3,但这就是您根据评论使用的。并且您可以在早期版本中轻松地手动实现它。

但是,物化视图是快照,不会动态更新。(无论如何,这可能会使任何性能优势无效。)要进行更新,请运行(昂贵的)操作:

REFRESH MATERIALIZED VIEW orphaned_products;
Run Code Online (Sandbox Code Playgroud)

您可以在战略上合适的时间点执行此操作,并根据您的业务模型让多个后续查询从中受益。

当然,您会在 上有一个索引orphaned_products.id,但这对于几百行的小表来说并不是很重要。

如果您的模型是永远不会删除交易的,您可以利用它来产生巨大的效果。手动创建一个类似的表:

CREATE TABLE orphaned_products2 AS
SELECT *
FROM   products p
WHERE  NOT EXISTS (SELECT 1 FROM transactions t WHERE t.product_id = p.id);
Run Code Online (Sandbox Code Playgroud)

当然,您可以通过截断和重新填充它来刷新“物化视图”,就像第一个一样。但关键是要避免昂贵的操作。你真正需要的是:

  • 将新产品添加orphaned_products2.
    触发器 实现AFTER INSERT ON products

  • 删除产品orphaned_products2只要引用行出现在表中transactions
    用触发器实现AFTER UPDATE OF product_id ON transations当您的模型允许transations.products_id更新时 - 这将是非常规的事情。
    还有一个AFTER INSERT ON transations

所有相对便宜的操作。

  • 如果交易也可以被删除,您需要另一个触发器来添加孤立产品AFTER DELETE ON transations——这会更贵一些。对于每个已删除的交易,您需要检查它是否是最后引用相关产品的交易,并在这种情况下添加一个孤立交易。可能仍然比刷新整个物化视图便宜很多。

VACUUM

您的附加信息后,我也建议对积极的吸尘自定义设置orphaned_products2,因为它会产生大量的死行。