web-dev-qa-db-ja.com

PostgreSQLでCOPY FROMを使用する場合、エスケープを無効にするにはどうすればよいですか?

PostgreSQL 9.5のテーブルに読み込みたい大きなタブ区切りファイルがあります。 通常の文字として扱いたい二重引用符とバックスラッシュが含まれています。

COPY FROMを使用する方法だと思いますが、エスケープを無効にする方法がわかりません。

これはデータのサンプルです(それは Googleのngramデータセット からのものです):

aX13_X  2006    8   5
aX13_X  2007    4   3
aX13_X  2008    2   1
a\  1852    1   1
a\  1935    1   1
a\  1937    2   2
ACT1V1T1ES  2003    15  11
ACT1V1T1ES  2004    63  6
ACT1V1T1ES  2005    1   1
ACT1V1T1ES  2006    5   4
ACT1V1T1ES  2008    4   3
ACTION="    1995    3   3
ACTION="    1996    6   5
ACTION="    1997    9   7
ACTION="    1998    19  11
ACTION="    1999    11  5

そしてテーブル:

CREATE TABLE onegram (
    id SERIAL,
    ngram character text,
    year integer NOT NULL,
    match_count integer NOT NULL,
    volume_count integer NOT NULL
);

修飾子なしで試すと、バックスラッシュがタブをエスケープします。

ngram=# copy onegram (ngram, year, match_count, volume_count)
from '/home/tims/data/ngram/test.tsv';
ERROR:  missing data for column "volume_count"
CONTEXT:  COPY onegram, line 4: "a\     1852    1       1"

だから私はCSVに切り替え、次に二重引用符文字がタブを引用します:

ngram=# copy onegram (ngram, year, match_count, volume_count)
from '/home/tims/data/ngram/test.tsv' WITH CSV DELIMITER E'\t';
ERROR:  unterminated CSV quoted field
CONTEXT:  COPY onegram, line 17: "ACTION="      1999    11      5
"

CSVを使用すると、DELIMITERキーワードを使用できます。サンプル(この場合はスペース)で発生しない区切り文字を選択すると機能します。

ngram=# copy onegram (ngram, year, match_count, volume_count)
from '/home/tims/data/ngram/test.tsv' WITH CSV DELIMITER E'\t' QUOTE E' ';
COPY 16

しかし、任意の文字を含めることができるようにしたい(タブと改行を除く)。 では、QUOTEを無効にするにはどうすればよいですか?または、COPY FROMの代わりに何を使用できますか?

編集:少し恣意的な理由で、理想的にはデータの前処理を含まないオプションが欲しいです。

5
Tim Smith

copyコマンドはデフォルトでtext形式を使用します。 tab デリミタ。したがって、必要なのはバックスラッシュをエスケープすることだけです。

copy onegram (ngram, year, match_count, volume_count)
from program 'sed ''s/\\/\\\\/g'' < /home/tims/data/ngram/test.tsv';
select * from onegram;
╔════╤════════════╤══════╤═════════════╤══════════════╗
║ id │   ngram    │ year │ match_count │ volume_count ║
╠════╪════════════╪══════╪═════════════╪══════════════╣
║  1 │ aX13_X     │ 2006 │           8 │            5 ║
║  2 │ aX13_X     │ 2007 │           4 │            3 ║
║  3 │ aX13_X     │ 2008 │           2 │            1 ║
║  4 │ a\         │ 1852 │           1 │            1 ║
║  5 │ a\         │ 1935 │           1 │            1 ║
║  6 │ a\         │ 1937 │           2 │            2 ║
║  7 │ ACT1V1T1ES │ 2003 │          15 │           11 ║
║  8 │ ACT1V1T1ES │ 2004 │          63 │            6 ║
║  9 │ ACT1V1T1ES │ 2005 │           1 │            1 ║
║ 10 │ ACT1V1T1ES │ 2006 │           5 │            4 ║
║ 11 │ ACT1V1T1ES │ 2008 │           4 │            3 ║
║ 12 │ ACTION="   │ 1995 │           3 │            3 ║
║ 13 │ ACTION="   │ 1996 │           6 │            5 ║
║ 14 │ ACTION="   │ 1997 │           9 │            7 ║
║ 15 │ ACTION="   │ 1998 │          19 │           11 ║
║ 16 │ ACTION="   │ 1999 │          11 │            5 ║
╚════╧════════════╧══════╧═════════════╧══════════════╝
5
Abelisto

データを前処理する必要があるようです。 COPYは、可能な入力を許可する場合、このデータを取り込むことができません。これは有効なCSVではなく、postgresのTSVに似たネイティブCOPY形式のルールにも従いません。 QUOTE NONEなどを設定するオプションはありません。

COPY ... FROM PROGRAM を使用して呼び出すデータを変換するPerl/pythonスクリプトのようなものをお勧めします。または、クライアント側の入力のためにデータをpsqlにパイプするか、データを変換してDBD::Pgpsycopg2などのクライアントドライバーを介してPostgresに直接フィードします。 COPYサポート。

いつでもpostgresパッチを提出して、次の人が同じ問題を簡単に解決できるようにすることができます。

3
Craig Ringer

@ Craig Ringerから、データを再フォーマットする必要があります。

あなたのデータに基づいて、Linuxでは、インポートする前にtrseqを使用してCSVファイルをフォーマットしました。

例:a.csv以下のファイル

aX13_X  2006    8   5
aX13_X  2007    4   3
aX13_X  2008    2   1
a\  1852    1   1
a\  1935    1   1
a\  1937    2   2
ACT1V1T1ES  2003    15  11
ACT1V1T1ES  2004    63  6
ACT1V1T1ES  2005    1   1
ACT1V1T1ES  2006    5   4
ACT1V1T1ES  2008    4   3
ACTION="    1995    3   3
ACTION="    1996    6   5
ACTION="    1997    9   7
ACTION="    1998    19  11
ACTION="    1999    11  5

CSVのフォーマット、引用符の無効化(複数のスペースを1つのスペースに置き換えてからスペース&\tから;)。 c.csvは結果ファイルです。ただし、小さなデータでテストしたため、入力データが大きい場合は確認してください。

cat a.csv | tr -s " " > b.csv;  sed -e 's/"//g' -e "s/[\t ]/;/g" b.csv  > c.csv 
cat c.csv
aX13_X;2006;8;5
aX13_X;2007;4;3
aX13_X;2008;2;1
a\;1852;1;1
a\;1935;1;1
a\;1937;2;2
ACT1V1T1ES;2003;15;11
ACT1V1T1ES;2004;63;6
ACT1V1T1ES;2005;1;1
ACT1V1T1ES;2006;5;4
ACT1V1T1ES;2008;4;3
ACTION=;1995;3;3
ACTION=;1996;6;5
ACTION=;1997;9;7
ACTION=;1998;19;11
ACTION=;1999;11;5

PGにインポート

psql -p 5432 -d postgres -c  "copy onegram (ngram, year, match_count, volume_count) from '/opt/c.csv' DELIMITER ';' CSV"

インポート後、結果は次のようになります。

id |   ngram    | year | match_count | volume_count
----+------------+------+-------------+--------------
 20 | aX13_X     | 2006 |           8 |            5
 21 | aX13_X     | 2007 |           4 |            3
 22 | aX13_X     | 2008 |           2 |            1
 23 | a\         | 1852 |           1 |            1
 24 | a\         | 1935 |           1 |            1
 25 | a\         | 1937 |           2 |            2
 26 | ACT1V1T1ES | 2003 |          15 |           11
 27 | ACT1V1T1ES | 2004 |          63 |            6
 28 | ACT1V1T1ES | 2005 |           1 |            1
 29 | ACT1V1T1ES | 2006 |           5 |            4
 30 | ACT1V1T1ES | 2008 |           4 |            3
 31 | ACTION=    | 1995 |           3 |            3
 32 | ACTION=    | 1996 |           6 |            5
 33 | ACTION=    | 1997 |           9 |            7
 34 | ACTION=    | 1998 |          19 |           11
 35 | ACTION=    | 1999 |          11 |            5
0
Luan Huynh