【问题标题】:Postgres UPDATE x WHERE id IN yPostgres 更新 x WHERE id IN y
【发布时间】:2012-11-02 21:05:31
【问题描述】:

这可能以前被问过,但谷歌搜索“IN”之类的关键字效果不佳。

这是我的查询:

UPDATE tblCustomer SET type = 2 
WHERE idcustomer                                
IN (SELECT fidcustomer1
    FROM tblorder                   
     UNION                      
    SELECT fidcustomer2
    FROM tblorder                   
   )                                

分解它:我想将出现在订单表中的所有客户的所有客户的类型(只是一个 int)设置为 2,位于任一列中。

在我的测试数据上,这些表中没有一个包含超过几百行,但查询运行了很多分钟(即使没有 UNION,这似乎也没有太大区别),显然是在重新做客户中每行一次内部查询。我显然可以将其重写为单个 SELECT DISTINCT(id),然后进行数百次单行更新,并以我用于 ODBC 访问的任何语言执行逻辑,但这只是一个 hack。

我怎样才能正确地重写这个?

附录:我要更新的表包含很多相对较大的 BYTEA blob,每行几 MB。它们设置为外部存储或扩展,但我想知道这是否会使顺序扫描变慢。所有更新似乎都需要很长时间,不仅仅是这一次。

【问题讨论】:

标签: sql postgresql union postgresql-performance


【解决方案1】:

我建议一个更简单的方法:

UPDATE tblCustomer c
SET    type = 2 
FROM   tblorder o
WHERE  c.idcustomer IN (o.fidcustomer1, o.fidcustomer2)
AND    c.type IS DISTINCT FROM 2  -- optional, to avoid empty updates

如果tblorder 中存在重复,那么与您所拥有的类似的方法是明智的:

UPDATE tblCustomer c
SET    type = 2 
FROM  (
    SELECT fidcustomer1 AS cust FROM tblorder
    UNION
    SELECT fidcustomer2 FROM tblorder
    ) o
WHERE  c.idcustomer = o.cust
AND    c.type IS DISTINCT FROM 2;

无论哪种方式,在 PostgreSQL 中,加入表通常比IN 表达式执行得更好。

【讨论】:

  • 在我的测试数据库中,重复项是稀疏的。在真实环境中,我预计部分客户负责高达 10% 到 20% 的订单并重复常态。
  • @Kdansky:然后进行第二个查询。 UNION 删除了骗子。任何剩余的性能问题都不是由于此查询引起的。你必须更仔细地检查你的桌子。触发器?并发写入负载很重?
  • 这个版本的执行速度足够快。谢谢。
【解决方案2】:
-------------------------------
-- Use two EXISTS:
-------------------------------
UPDATE tblCustomer tc
SET type = 2
WHERE EXISTS (
    SELECT *
    FROM tblorder ex
    WHERE ex.fidcustomer1 = tc.idcustomer
    )
OR EXISTS (
    SELECT *
    FROM tblorder ex
    WHERE ex.fidcustomer2 = tc.idcustomer
    );

-------------------------------
-- or combine the two EXISTS::
-------------------------------
UPDATE tblCustomer tc
SET type = 2 
WHERE EXISTS (
    SELECT *
    FROM tblorder ex
    WHERE ex.fidcustomer1 = tc.idcustomer
    OR ex.fidcustomer2 = tc.idcustomer
    );

我的直觉是第一个版本(有两个单独的存在)会执行得更好,因为如果其中一个存在会产生True,则执行程序可能短路。这将避免 UNION 构造固有的删除重复阶段(可能还有排序)。

【讨论】:

  • 有很多重复项,这可能比我的解决方案更快。仅 10 - 20 % 可能不会。布丁的证据在吃。
  • 问题是,如果没有实际的重复项,也必须进行删除重复项(计划者不知道这一点)。将始终需要排序步骤(N log N)(除非要处理非常少量的数据,并且可以使用散列或位图/索引操作)。一个小实验表明我的两个存在版本总是最快的。有时 Erwin#2 大致相同(有时甚至产生相同的查询计划)。注意:我在 FK1 和 FK2 上添加了索引。
  • 我已将我的实现切换为您的版本的变体。它们在我的数据上的表现都非常相似,但这个适用于 SQLite,而 UPDATE FROM 变体则不能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-13
  • 2018-05-16
  • 1970-01-01
  • 1970-01-01
  • 2015-10-23
  • 1970-01-01
相关资源
最近更新 更多