2

私はPostgreSQLで関数を書いています。それは基本的に3つのステップを行います:

  1. ソース テーブルからレコードを取得します。
  2. ターゲット テーブルでフェッチされたレコードの値を確認し、ターゲット テーブルでレコードが見つかった場合は、フェッチされたレコードでターゲット テーブルのすべての値を更新します。そうでない場合は、フェッチされたレコードをターゲット テーブルに挿入します。

このループを実行する代わりに、挿入/更新用の単一のクエリを作成すると、上記のアプローチよりも高速になりますか? すべてのレコードをループして更新/挿入を行う代わりに、単一のクエリを記述して同じ結果を得るにはどうすればよいですか。

私の現在のアプローチは以下の通りです

CREATE OR REPLACE FUNCTION fun1()
  RETURNS void AS
$BODY$DECLARE
   source_tab_row RECORD;

   v_col1 TEXT;
   v_col2 TEXT;
   v_col3 TEXT;
   v_col4 double precision ;
   cnt integer;

BEGIN
    FOR source_tab_row IN (SELECT * FROM source_tab where col5='abc')
LOOP
    v_col1=source_tab_row.col1;   
    v_col2=source_tab_row.col2;
    v_col3=source_tab_row.col3;
    v_col4=source_tab_row.col4;

    select count(*) INTO cnt from dest_tab where col1=v_col1;

     if (cnt =0) then
     -- If records is not found
       INSERT INTO dest_tab(col1, col2, col3,col4)
       VALUES( v_col1, v_col2, v_col3,v_col4)   ;
    else
     --if records found then update it
       update dest_tab set col1=v_col1, col2=v_col2, col3=v_col3,col4=v_col4
       where col1=v_col1;

     end if;         
END LOOP;
END;
$BODY$ LANGUAGE plpgsql;
4

1 に答える 1

2

より良いSQL

PostgreSQL 9.1以降を使用している場合は、これにデータ変更CTEを使用する必要があります。

WITH x AS (
   UPDATE dest_tab d
   SET    col2 = s.col2
        , col3 = s.col3
   --   , ...
   FROM   source_tab s
   WHERE  s.col5 = 'abc'
   AND    s.col1 = d.col1

   RETURNING col1
   )
INSERT INTO dest_tab(col1, col2, col3, col4)
SELECT s.col1, s.col2, s.col3, s.col4
FROM   source_tab s
WHERE  s.col5 = 'abc'
LEFT   JOIN x USING (col1)
WHERE  x.col1 IS NULL;

@Craigがすでに投稿しているように、このような操作は、個々の行を反復処理するよりも、セットベースのSQLとして定期的にはるかに高速です。

ただし、この形式はより高速で単純です。また、固有の(小さな!)競合状態を大幅に回避します。まず、これは単一のSQLコマンドであるため、タイムスロットはさらに短くなります。また、同時トランザクションがUPDATEとの間で競合する行を入力する必要がINSERTある場合、重複キー違反が発生します(必要に応じてpk /一意の制約がある場合)。2回目のクエリを行わずdest_tab、の元のセットを再利用するためINSERTです。より速く、より良い。

キー違反が重複している場合は、何も問題は発生していません。クエリを再試行してください。

その間に並行トランザクションが行になる反対のケースについては説明していません。DELETEこれは実際にはそれほど重要ではない/頻繁なケースではありません、IMO。

適切なplpgsql

これにplpgsqlを使用する場合は、以下を単純化してください。

CREATE OR REPLACE FUNCTION fun1()
  RETURNS void AS
$BODY$
DECLARE
   _source source_tab;  -- name of table = type
BEGIN
   FOR _source IN
      SELECT * FROM source_tab where col5 = 'abc'
   LOOP
        UPDATE dest_tab
        SET    col2 = _source.col2  -- don't update col1, it doesn't change
               ,col3 = _source.col3
               ,col4 = _source.col4
        WHERE  col1 = _source.col1;

      IF NOT FOUND THEN  -- no row found
         INSERT INTO dest_tab(col1, col2, col3,col4)
         VALUES (_source.col1, _source.col2, _source.col3, _source.col4);
      END IF;

   END LOOP;
END
$BODY$ LANGUAGE plpgsql;
于 2012-11-05T18:25:23.887 に答える