【问题标题】:Update multiple rows in a table with different values用不同的值更新表中的多行
【发布时间】:2016-06-30 15:09:42
【问题描述】:

我有以下架构:

DROP SCHEMA IF EXISTS s CASCADE;  
CREATE SCHEMA s;

CREATE TABLE "s"."t1"
(
    "c1" BigSerial PRIMARY KEY,
    "c2" BigInt NOT NULL,
    "c3" BigInt
)
WITH (OIDS=FALSE);

INSERT INTO s.t1 (c2, c3) VALUES (10, 100);
INSERT INTO s.t1 (c2, c3) VALUES (20, 200);
INSERT INTO s.t1 (c2, c3) VALUES (30, 300);
INSERT INTO s.t1 (c2, c3) VALUES (40, 400);

PREPARE updateplan (BigInt, BigInt) AS 
    update s.t1 
    SET c3 = $2
    WHERE c2 = $1;

EXECUTE updateplan (20, 250);

PREPARE updatearrayplan(BigInt[], BigInt[]) AS
    for i in size($1)
    DO  
        update s.t1
        SET c3 = $2[$i]
        WHERE c2 = $1[$i]
    END FOR    

EXECUTE updatearrayplan({20, 30}, {275, 375})

/* 20, 200 -> 20, 275 */
/* 30, 300 -> 30, 375 */

执行 updatearrayplan 后,我希望这些行具有这些值 20 -> 275 , 30 -> 375

有没有办法用作为数组传入的不同列值更新多行。还可以保证数组的顺序将保持不变。

【问题讨论】:

    标签: database postgresql sql-update multiple-columns


    【解决方案1】:

    试试:

    WITH arrays AS( 
        SELECT * from 
        unnest(
             ARRAY[20, 30], 
             ARRAY[275, 375]
        ) as xy(x,y)
    )
    UPDATE t1 
    SET c3 = a.y 
    FROM arrays a
    WHERE c2 = a.x;
    

    在这里查看unnest函数的描述:click


    编辑


    @kordiroko 抱歉。我尝试了一整天来修改您的解决方案。 无法让它工作。

    可能是您的 PostgreSQL 版本较旧。我在 9.5 版本上对其进行了测试,我只花了几分钟就让它工作了,只需复制/粘贴并更改查询中的两个参数:

    create table t1(
      c2 BIGINT,
      c3 bigint
    );
    
    
    insert into t1( c2, c3 )
    select x, x * 100 
    from generate_series( 1,1000000 ) x;
    
    CREATE OR REPLACE FUNCTION updatefunc1(BigInt[], BigInt[])
    RETURNS void as $$
    BEGIN
        FOR i IN array_lower($1, 1) .. array_upper($1, 1)
        LOOP  
            update t1
            SET c3 = $2[i]
            WHERE c2 = $1[i];
        END LOOP;  
    END;    
    $$
    LANGUAGE plpgsql;
    
    
    CREATE OR REPLACE FUNCTION updatefunc2(BigInt[], BigInt[])
    RETURNS void as $$
    BEGIN
        WITH arrays AS( 
            SELECT * from 
            unnest( $1, $2  ) as xy(x,y)
        )
        UPDATE t1 
        SET c3 = a.y 
        FROM arrays a
        WHERE c2 = a.x;
    END;    
    $$
    LANGUAGE plpgsql;
    
    
    select updatefunc1(ARRAY[20], ARRAY[275]);
    
    select updatefunc2(ARRAY[30], ARRAY[555]);
    
    select * from t1 where c2 in (20,30);
    

    让我知道这是正确的还是有更好的解决方案。

    这是非常正确的,但是......它有点慢。

    我仅针对 100 条记录测试了您的函数:

    select updatefunc1(
        array( select * from generate_series(1,100)),
        array( select 22222 from generate_series(1,100))
    );
    

    耗时超过 12 秒:

    结果(成本=20.00..20.31 行=1 宽度=0)(实际 time=12259.095..12259.096 rows=1 loops=1) 输出: updatefunc1(($0)::bigint[], ($1)::bigint[]) InitPlan 1(返回 $0)

    现在将它与我的函数进行比较,但是对于 100.000 条记录:

    select updatefunc2(
        array( select * from generate_series(1,100000)),
        array( select 22222 from generate_series(1,100000))
    );
    

    结果是 1 秒 150 毫秒:

    结果(成本=20.00..20.31 行=1 宽度=0)(实际 time=1150.018..1150.123 rows=1 loops=1) 输出: updatefunc2(($0)::bigint[], ($1)::bigint[]) InitPlan 1(返回 $0)

    以上结果意味着,您的功能是:

    ( 12 / 100 ) / ( 1.150 / 100000 ) = 10434,78

    时间 slooooooooooooooooooooooooooweeeeeeeeeeeeeeeeeeeeeeer,
    以 % 为单位,这只是 1043400 %


    编辑 2


    我的版本是 9.2.15。它抛出语法错误

    以下是适用于早期 PostgreSQL 版本的版本:

    CREATE OR REPLACE FUNCTION updatefunc3(BigInt[], BigInt[])
    RETURNS void as $$
    BEGIN
        WITH arrays AS( 
            SELECT arr1[ rn ] as x, arr2[ rn ] as y 
            FROM (
                SELECT $1 as arr1, $2 as arr2, generate_subscripts($1, 1) As rn
            ) x
        )
        UPDATE t1 
        SET c3 = a.y 
        FROM arrays a
        WHERE c2 = a.x;
    END;    
    $$
    LANGUAGE plpgsql;
    
    select updatefunc3(ARRAY[40,82,77], ARRAY[333,654]);
    
    select * from t1 where c2 in (40,82,77);
    

    对 uptadint 100,000 行的速度测试是:

    select updatefunc3(
        array( select * from generate_series(1,100000)),
        array( select 22222 from generate_series(1,100000))
    );
    

    结果(成本=20.00..20.31 行=1 宽度=0)(实际 time=1361.358..1361.460 rows=1 loops=1) 输出: updatefunc3(($0)::bigint[], ($1)::bigint[]) InitPlan 1(返回 $0)

    更新100k行的时间在1.5秒以下


    编辑 3


    @kordiko:你能告诉我为什么你的查询要好得多。 我的函数遍历每一行并一一更新元素。 您的功能似乎也做同样的事情。难道所有的 等效行在您的查询中同时更新。

    这是因为我的函数只运行一个更新命令,而不管数组中的元素数量如何,而您的函数一个一个地更新元素 - 对于 100 个元素,它运行 100 个更新命令。对于 1000 个元素,它会运行 1000 个更新命令。
    我已经在一个有 1000000 行但没有任何索引的表上完成了测试。在我的函数中,更新只读取一次表内容(进行全表扫描),并更新匹配的行。您的函数执行 100 次更新,每次更新都会执行一次全表扫描。
    如果您在col2 上创建和索引,那么您的函数的速度会显着提高,请参见下面的测试(请注意,此测试中的元素数量从 100 增加到 100000:

    create INDEX t1_c2_ix on t1( c2 );
    
    select updatefunc1(
        array( select * from generate_series(1,100000)),
        array( select 22222 from generate_series(1,100000))
    );
    
    Result  (cost=20.00..20.31 rows=1 width=0) (actual time=**3430.536**..3430.636 rows=1 loops=1)
      Output: updatefunc1(($0)::bigint[], ($1)::bigint[])
      InitPlan 1 (returns $0)
    

    现在时间只有大约 3.5 秒。

    并在创建索引后测试我的功能:

    select updatefunc3(
        array( select * from generate_series(1,100000)),
        array( select 22222 from generate_series(1,100000))
    );
    

    结果(成本=20.00..20.31 行=1 宽度=0)(实际时间=1270.619..1270.724 行=1 循环=1) 输出:updatefunc3(($0)::bigint[], ($1)::bigint[]) InitPlan 1(返回 $0)

    时间保持不变,但仍比您的函数快 100%。

    【讨论】:

    • @kordiroko 对不起。我尝试了一整天来修改您的解决方案。无法让它工作。
    • @kordiko :请告诉我为什么您的查询要好得多。我的函数遍历每一行并一一更新元素。您的功能似乎也做同样的事情。是否在您的查询中同时更新了所有等效行。
    【解决方案2】:

    我的回答:

    CREATE OR REPLACE FUNCTION s.updatefunc1(BigInt[], BigInt[])
    RETURNS void as $$
    BEGIN
        FOR i IN array_lower($1, 1) .. array_upper($1, 1)
        LOOP  
            update s.t1
            SET c3 = $2[i]
            WHERE c2 = $1[i];
        END LOOP;  
    END;    
    $$
    LANGUAGE plpgsql;
    
    select s.updatefunc1(ARRAY[20], ARRAY[275]);  
    

    这确实有效。我得到了我想要的答案:

       SELECT c2, c3 FROM s.t1;
       c2 | c3  
       ----+-----
       10 | 100
       30 | 300
       40 | 400
       20 | 275  --> Updated
    

    让我知道这是正确的还是有更好的解决方案。

    【讨论】:

      猜你喜欢
      • 2011-04-22
      • 2012-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-08
      • 2016-11-17
      • 1970-01-01
      相关资源
      最近更新 更多