在给定一组唯一列值的情况下,更新Postgres表的最快方法?

时间:2014-01-13 23:31:31

标签: postgresql

我在尝试执行Postgres更新时反复遇到同样的问题。首先,我将运行SELECT查询,如下所示:

SELECT stock_number
  FROM products
  WHERE available = true
EXCEPT
SELECT stock_number
  FROM new_inventory_list;

这将选择所有产品的库存号,表明它们在当前数据库中可用,但不再出现在刚刚下载的新库存清单中。此命令运行得非常快。但是,实际上我用来更新此列表的任何方法似乎都需要至少十分钟才能运行,从而减慢了服务器的速度。例如:

UPDATE products
  SET available = false
  WHERE stock_number IN (
    SELECT stock_number
      FROM products
      WHERE available = true
      AND stock_number IS NOT NULL
    EXCEPT
    SELECT stock_number
      FROM new_inventory_list
    );

通常需要更新至少10,000行,如果供应商一次推送大量新库存,通常会更多。此外,我们需要检查价格更新。获得价格变化的产品的库存清单相对快速且容易:

WITH overlap AS (
  SELECT stock_number
    FROM products
  INTERSECT
  SELECT stock_number
    FROM new_inventory_list
  )
unchanged AS (
  SELECT stock_number, price
    FROM products
  INTERSECT
  SELECT stock_number, price
    FROM new_inventory_list
  )
SELECT * FROM overlap EXCEPT SELECT stock FROM unchanged;

对于此查询,我甚至不尝试使用SQL命令来执行此操作,而是将列表拉出到脚本中,然后分别对每个修改后的值运行UPDATE。它很慢,但似乎仍然比我在SQL中严格执行的任何命令都要快。另外,使用外部脚本,我可以定期输出进度,所以我估计它将运行多长时间。股票编号是独一无二的,尽管它们偶尔会为空。 (那些应该被忽略)

我觉得必须有一个更快的方法来做到这一点,但到目前为止,我没有任何运气搞清楚。有什么想法吗?

编辑:

我认为我找到了比这个问题更好的解决方案:

WITH removed AS (
  SELECT stock_number
    FROM products
    WHERE available = true
  EXCEPT
  SELECT stock_number
    FROM new_inventory_list
  )
UPDATE products AS p
  SET available = false
  FROM removed
  WHERE removed.stock_number = p.stock_number;

我没有考虑过一起使用UPDATEWITH的想法,甚至在我阅读Postgres的UPDATE文档之前甚至都不知道这是可能的。虽然速度要快得多,但仍然需要几分钟才能运行,所以为了监控它,我只是在循环中运行上面的命令,在LIMIT 1000子句的末尾加SELECT,打印一个每次成功更新另一个块时都会向控制台发送消息。

2 个答案:

答案 0 :(得分:2)

此查询:

WITH removed AS (
  SELECT stock_number
    FROM products
    WHERE available = true
  EXCEPT
  SELECT stock_number
    FROM new_inventory_list
  )
UPDATE products AS p
  SET available = false
  FROM removed
  WHERE removed.stock_number = p.stock_number;

...我相信,会自行与 整个 表进行多余的联接。由于except语句中的with子句,可能表现不佳。

以这种方式思考:假设一个产品表有一百万行,大约250k标记为可用,而50k那些没有出现在200k项强库存清单中。 with查询运行如下:1)找到需要更新的产品中的50k行; 2)然后,对于产品中的每一行,检查id是否在那些50k行中以便 重新 -select < / em>那些相同的50k行; 3)并更新行。

为了提高性能,更新查询应从需要直接直接的产品中选择候选行,并使用反连接来消除不需要的行。之前发布的@wildplasser查询似乎很好:

UPDATE products dst
SET available = false
WHERE available
AND NOT EXISTS (
    SELECT 1
    FROM new_inventory_list nx
    WHERE nx.stock_number = dst.stock_number
    );

另一点是你在评论中提到的“大约50列,其中20个已编入索引”:这将大大降低更新速度。想象一下:每次更新的行都需要写入那个表,而不是写入另外的 20 表中。你确定这不应该更加规范化,你真的需要每个索引吗?

答案 1 :(得分:0)

你试过吗

WITH removed AS (
  SELECT stock_number
    FROM products p1
    LEFT JOIN new_inventory_list n1
    ON p1.stock_number=n1.stock_number
    WHERE p1.available AND n1.stock_number IS NULL
  )

我不知道EXCEPT是如何完成的;也许这将保留一些索引,以便在UPDATE中使用。此外,如果available通常为false,我会添加部分索引

CREATE INDEX product_available ON product(stock_number) WHERE available;