进行高效的横向连接(或替代方案)

Making Efficient Lateral Joins (or alternatives)

上下文

我正在尝试使用 PostgreSQL 的 lateral 连接,特别是在组 by/limit 上执行连接。

当我查找单个记录时,查询工作得非常好,但当我们查询多个记录时,性能会很快下降。这是有道理的,因为我们有多个子查询 运行 个人收集,过滤聚合,sorts.the 问题是,我们应该看什么 Postgres 策略,或者我们如何重构下面的查询以使其在规模?

查询

我们有三个主要 table,其中两个之间有一个连接点 table:

|Managers| >- |Stores| >- |Store_Products| -< Products

我们有给定商店记录的所有历史经理,我们有商店的完整产品目录(产品可能由多个商店携带)。

目标:给定商店 ID,查询最近的经理和最近的 产品已售出。

这是从 Store 到 Manager 和 Product 的内部联接。经理和产品必须按日期 desc 排序并限制为 1(至少我相信这是获取最新的方式)。

SELECT 
    store.id as store_id,
    manager.id as manager_id,
    *
FROM 
    Stores as store,
    LATERAL (
        SELECT 
            * 
        FROM 
            Products as product 
        INNER JOIN Stores_Products store_product on store_product.product_id = product.id
        WHERE 
            store_product.store_id = store.id
        ORDER BY 
            store.date desc
        LIMIT 1
    ) p,
    LATERAL (
        SELECT 
            * 
        FROM 
            Managers as manager
        WHERE 
            manager.store_id = store.id 
        ORDER BY
            manager.date desc
        LIMIT 1
        ) m
WHERE store.name = 'ABC retail'

这在您查询单个商店时非常有效。但是,如果您尝试批量查询(例如 WHERE store.name in [...]),查询会变得非常缓慢并且内存消耗非常快。

问题

是否有更好的方法来查询可扩展的数据?

谢谢!

注意:stores/products给出的例子只是一个沟通问题的工具。实际的模式是不同的 - 所以我要求不要过多考虑这是否是规范化模式的最佳方式!谢谢!

也许 window 功能会运行得更快。在下面的代码中,产品订购属性保留为 ...,因为在您的代码段中,它们似乎是由 store.date 订购的,这看起来是错误的(它是商店的 属性,而不是产品的而不是商店出售的产品)。

SELECT * FROM 
-- Let's rank managers within each store, giving rank=1 to the most recent
(
  SELECT id, 
         store_id, 
         RANK() OVER (PARTITION BY store_id ORDER BY date DESC) AS mgr_rank
  FROM Manager
) AS MgrRank 

JOIN

-- Let's rank products within each store, giving rank=1 to the most recent
(
  SELECT store_id,
         Products.*
         RANK() OVER (PARTITION BY store_id ORDER BY .... DESC) AS product_rank
  FROM Stores_Products JOIN Products ON product_id = Products.id
) AS ProductRank
USING(store_id) 

-- Now let's join stores themselves
JOIN Stores ON store_id = Stores.id

-- Select most recent manager and product
WHERE mgr_rank=1 AND product_rank=1 AND Stores.name='ABC retail'

请记住,此特定查询不会输出没有经理或产品的商店。您还需要使用外部联接来包含它们。