进行高效的横向连接(或替代方案)
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'
请记住,此特定查询不会输出没有经理或产品的商店。您还需要使用外部联接来包含它们。
上下文
我正在尝试使用 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'
请记住,此特定查询不会输出没有经理或产品的商店。您还需要使用外部联接来包含它们。