2017-10-25 12 views
0

COPYコマンドを使用して、ファイルからPython経由でPGSQLにデータを挿入しようとしています。Python PostgreSQL(INSERTだけでなく)INSERTまたはUPDATEに使用されるCOPYコマンド

cmd = ("COPY %s (%s) FROM STDIN WITH (FORMAT CSV, NULL '_|NULL|_')" % 
       (tableName, colStr)) 
cursor.copy_expert(cmd, io) 

私は最初のテーブルを空にすることなく、このCOPYコマンドを実行できるようにしかし好む:ターゲット表が空であるか、私は一意のキーの衝突は存在しません事前に確保するとき、これは非常によく動作します。 SQL COPYで 'INSERTまたはUPDATE'型操作を実行する方法はありますか?

ソリューションを更新

COPYが完了した後、私は(Djangoのメソッド内で)私のCSV COPYデータ・ソースに「tmpDom」の値を強制して、次のようにやってしまいました。

ここの_domainフィールドは、私の特定のスキーマの論理グループです。それは、私はダミーの一時的な値の下にすべての新しいデータを読み込むことができますし、1つのトランザクションで私たちは新しいデータの値を更新しながらそれを置き換えてすべての古いデータを削除します。

with transaction.atomic(): 
     klas.objects.filter(_domain=curObj).delete() 
     klas.objects.filter(_domain=tmpDom).update(_domain=curObj) 
+0

つのオプション::/:1)2)[ 'file_fdw'](HTTPSを使用する一時テーブルにコピーして、そこからアップサートを行うただし、個々のアイテムをラップするためにcolStrを分解する必要があります/www.postgresql.org/docs/current/static/file-fdw.html)extension –

答えて

1

copyコマンドを直接使用することはできません。

ただし、一時テーブルを作成し、そのテーブルにcopyコマンドを入力してから挿入してから更新してください。

-- Clone table stucture of target table 
create temporary table __copy as (select * from my_schema.my_table limit 0); 


-- Copy command goes here... 


-- Update existing records 
update 
    my_schema.my_table 
set 
    column_2 = __copy.column_2 
from 
    __copy 
where 
    my_table.column_1 = __copy.column_1; 


-- Insert new records 
insert into my_schema.my_table (
    column_1, 
    column_2 
) (
    select 
     column_1, 
     column_2 
    from 
     __copy 
     left join my_schema.my_table using(column_1) 
    where 
     my_table is null 
); 

あなたは、更新クエリのアップを高速化するデータとそれを移入した後__copyにインデックスを作成することを検討することがあります。

+0

ありがとう、それはまさに私がやったことです。 (原則として...私はDjangoの中で遊んでいるので詳細は少し違うので、私はそのメカニックをいくつか使っていましたが、同じコンセプトです) – slumtrimpet

+1

注意してください。 Postgresには['ON CONFLICT'](https://www.postgresql.org/docs/current/static/sql-insert)があります。html#SQL-ON-CONFLICT)を使用して、MySQLの「UPSERT」やSQL Server/Oracleの「MERGE」と同様の追加クエリと更新クエリを組み合わせることができます。 – Parfait

+0

@ Parfait私は常に物事が存在することを忘れています:) – Scoots

0

すでに既存のテーブルにデータを追加することはできませんなし理由があるPostgreSQLのドキュメントを参照:ファイルからのコピーデータから https://www.postgresql.org/docs/9.6/static/sql-copy.html

COPYテーブルに( 何にデータを追加すでにテーブルに入っています)

だから私はどこか別のエラーがあると思います。テーブルに二度目のデータを挿入しようとすると、PostgreSQLから受け取ったメッセージに関する詳細を教えてください。

+0

ソースファイルとターゲットテーブルの間に主キーの競合があると、クエリ全体が失敗する原因になります。また、該当する場合は既存のレコードを更新することはありませんが、挿入するだけです。 – Scoots

+0

データに一意のキーの衝突があると、COPYコマンドがエラーをスローします。私の質問は、ちょうど挿入を試みるのではなく、衝突した行を更新しながら、このようなデータを一括ロードする他の方法がある場合に、より関連していました。 – slumtrimpet

+0

この場合、残念ながらCOPYはありません。この場合の最良の方法は、一時テーブルをコピーし、メインテーブルの既存のレコードを更新し、メインテーブルに既存のレコードを挿入しないことです。 –

0

csvファイルデータを受信するステージングテーブルとして一時テーブルを使用することを検討してください。次に、PostgresのCONFLICT (colname) DO UPDATE ...を使用して最終テーブルに追加を実行します。バージョン9.3以降で利用可能です。 docsを参照してください。特別な除外テーブルは、最初に挿入用に提案された値を参照するために使用されます。

また、pyscopg2を使用する場合は、sql.Identifier()を使用して、テーブル名や列名などの識別子を安全にバインドすることを検討してください。

from psycopg2 import sql 
... 
cursor.execute("DELETE FROM tempTable") 
conn.commit() 

cmd = sql.SQL("COPY {0} ({1}) FROM STDIN WITH (FORMAT CSV, NULL '_|NULL|_'))")\ 
       .format(sql.Identifier(temptableName), 
         sql.SQL(', ').join([sql.Identifier('col1'), 
              sql.Identifier('col2'), 
              sql.Identifier('col3')])) 
cursor.copy_expert(cmd, io) 

sql = "INSERT INTO finalTable (id_column, Col1, Col2, Col3)" + \ 
     " SELECT id_column, Col1, Col2, Col3 FROM tempTable t" + \ 
     " ON CONFLICT (id_column) DO UPDATE SET Col1 = EXCLUDED.Col1," + \ 
     "          Col2 = EXCLUDED.Col2," + \ 
     "          Col3 = EXCLUDED.Col3 ...;" 

cursor.execute(sql) 
conn.commit() 
関連する問題