2016-06-30 23 views
0

I次のスキーマを持っている:更新複数行

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

です210

配列として渡される異なる列の値を持つ複数の行を更新する方法はありますか。また、配列の順序が維持されるという保証もあります。

答えて

1

試してみてください。click


EDIT


@kordiroko申し訳ありません:

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機能の説明を参照してください。あなたのソリューションを変更して一日中使いました。 動作させることができませんでした。

古いPostgreSQLバージョンを使用している可能性があります。これがあれば、私に教えてください

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); 

:私は、バージョン9.5でそれをテストし、それだけで、コピー/貼り付け、それが仕事を得るために私に数分だけのカップルを取り、クエリ内の2つのパラメータを変更します正しいか、より良い解決策があります。

これはちょっと遅いです。それが終わる12秒かかりました

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



は、私が唯一の100レコードのためにあなたの機能をテストし

の検索結果を(コスト= 20.00..20.31行= 1、幅= 0)(実際 時間= 12259.095..12259.096行= 1つのループ= 1)出力: updatefunc1(($ 0):: BIGINT []、($ 1):: BIGINT [])InitPlan 1(戻り$ 0)

は今私の関数にそれを比較するが、100.000レコードについて:

結果(コスト= 20.00..20.31行= 1、幅= 0):

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

結果は、1秒150ミリ秒であります(実際の 時間= 1150.018 ..1150.123行= 1つのループ= 1)出力: updatefunc2(($ 0):: BIGINT []、($ 1):: BIGINT [])InitPlan 1($ 0を返し)

上記の結果の意味、そのあなたの関数は次のとおりです。

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

回のslooooooooooooooooooooooooooweeeeeeeeeeeeeeeeeeeeeeer、
および%で、これは私のバージョンは9.2.15です


EDIT 2


のみ104.34万%遅くなります。

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)) 
); 

それは以下

のPostgreSQLの以前のverionsに取り組む必要があるバージョンです構文エラーをスローします結果(コスト= 20.00..20.31行= 1つの幅= 0)(実際の 時間= 1361.358 ..1361.460行= 1つのループ= 1)出力: updatefunc3(($ 0):: BIGINT []、($ 1):: BIGINT [])InitPlan 1($ 0を返します)

100kの行を更新する時間が1.5秒


を下回っています

EDIT 3


@kordiko:あなたのクエリがそんなに優れている理由を教えてくださいでした。 私の関数は各行を通り、要素を1つずつ更新します。 あなたの関数も同じように見えます。それはすべての 同等の行がクエリで同時に編を更新していることです。

あなたの関数は要素一つ一つを更新しながら、私の関数は、関係なく、配列の要素数の一つだけ updateコマンドを実行しますので、これはです - 100個の要素のためには、100の更新コマンドを実行します。 1000要素に対して、1000更新コマンドを実行します。
私はなく、任意のインデックスのない、1000000行をテーブルの上に私のテストを行ってきました。私の機能では、更新プログラムはテーブルの内容を1回だけ読み込み(テーブル全体のスキャンを行います)、一致する行を更新します。関数は100回の更新を実行し、それぞれが完全な表スキャンを行います。
作成およびインデックスcol2に、そしてあなたの関数の速度は、この試験の要素数が100000に100から増加していることに注意してください(下記のテストを参照してください、劇的inceases場合:今

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%速いです。

+0

@kordiroko申し訳ありません。あなたのソリューションを変更して一日中使いました。それを動作させることができませんでした。 – gudge

+0

私はアンガーを更新しました。それを見てください。 – krokodilko

+0

私のバージョンは9.2.15です。構文エラーが発生します。 – gudge

0

私の答え:

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 

これが正しいか、より良い解決策があるかどうか教えてください。