Postgres UPDATE x WHERE id in y

时间:2012-11-14 11:07:24

标签: sql postgresql union postgresql-performance

之前可能已经提出过这个问题,但谷歌搜索“IN”这样的关键字效果不佳。

这是我的疑问:

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

要将其分解:我想将所有客户的类型(只是一个int)设置为2,对于出现在order-table中的所有客户,在任一列中。

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

如何正确地重写?

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

2 个答案:

答案 0 :(得分:5)

我建议采用一种更简单的方法:

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表达更好。

答案 1 :(得分:4)

-------------------------------
-- 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构造所固有的。