web-dev-qa-db-ja.com

動的な列数の一時テーブルにCSVファイルをコピーしていますか?

列の数がcsvファイルで不明な一時テーブルにcsvファイルをコピーする方法があるかどうか疑問に思っています。私が使用しているDBソフトウェアはPgAdmin IIIです。列の数がわかっている場合は、その列数の一時テーブルを作成し、csvファイルを次のようにコピーできることを理解しました。

   CREATE TEMPORARY TABLE temp
   (
      col1 VARCHAR(80),
      col2 VARCHAR(80),
       ....
      coln VARCHAR(80)
   );

COPY temp FROM 'C:/Users/postgres/Boost.txt' CSV HEADER DELIMITER E'    '

ただし、一時テーブルに列のない一時テーブルにcsvファイルを単にコピーしようとすると、Postgresql(バージョン8.4)は、csvファイルよりも列が少ないテーブルで作業していると不平を言います。私は調査しており、これについてPostgresqlのドキュメントで何も見つけることができないようです。 Postgresqlで実行時に決定された任意の数の列を持つ一時テーブルにcsvファイルをコピーできるかどうか誰かが知っていますか?一時テーブルにcsvファイルが読み込まれたら、破棄される前に一時テーブルを使用して他のテーブルとの比較を行う予定です。また、csvファイルの最初の行にはヘッダーが含まれています。

4
Bmoe

基本:

  • CSVファイルの1行目には、定義された形式の列名があります。
  • PROGRAMの後のCOPYおよびGET DIAGNOSTICSCOPY句には、Postgres9.3 +が必要です。
  • format() Postgresが必要9.1 +
  • これは純粋な標準Postgresで動作します-シェルが提供すると予想されるheadコマンドを除きます。 Windowsバージョンの場合、以下を考慮してください。

完全自動化

この関数はanyテーブル構造を完全に動的にコピーします

CREATE OR REPLACE FUNCTION f_dynamic_copy(_file    text
                                        , _tbl     text = 'tmp1'
                                        , _delim   text = E'\t'
                                        , _nodelim text = chr(127)) -- see below!
  RETURNS text AS
$func$
DECLARE
   row_ct int;
BEGIN
   -- create staging table for 1st row as  single text column 
   CREATE TEMP TABLE tmp0(cols text) ON COMMIT DROP;

   -- fetch 1st row
   EXECUTE format($$COPY tmp0 FROM PROGRAM 'head -n1 %I' WITH (DELIMITER %L)$$  -- impossible delimiter
                , _file, _nodelim);

   -- create actual temp table with all columns text
   EXECUTE (
      SELECT format('CREATE TEMP TABLE %I(', _tbl)
          || string_agg(quote_ident(col) || ' text', ',')
          || ')'
      FROM  (SELECT cols FROM tmp0 LIMIT 1) t
           , unnest(string_to_array(t.cols, E'\t')) col
      );

   -- Import data
   EXECUTE format($$COPY %I FROM %L WITH (FORMAT csv, HEADER, NULL '\N', DELIMITER %L)$$
                , _tbl, _file, _delim);

   GET DIAGNOSTICS row_ct = ROW_COUNT;
   RETURN format('Created table %I with %s rows.', _tbl, row_ct);
END
$func$  LANGUAGE plpgsql;

コールバリアント:

SELECT f_dynamic_copy('/path/to/file.csv');
SELECT f_dynamic_copy('/path/to/file2.csv', 'tmp_file2');
SELECT f_dynamic_copy(_file  => '/path/to/file2.csv'
                    , _tbl   => 'tmp_file2');
                    , _delim => E'\t'); -- using assignment operator since pg 9.5

回答:

123行のテーブルtmp_file2を作成しました。

メインのCOPYの前に、予備のCOPY ... TO tmp0を実行して、列名を含む最初の行をフェッチします。これは、引用符で囲まれていないことが想定され、COPY ... TO ... (FORMAT csv, HEADER)などの大文字と小文字を区別する文字列はそれらをエクスポートします。

実際のターゲットテーブルの構造は、すべての列がデータ型textであるテーブルから派生します。結果のテーブルのデフォルト名はtmp1です-または、独自の2番目の関数パラメーターを指定します。

次に、COPYが実行されます。デフォルトの区切り文字はタブ文字です。または3番目の関数パラメーターとして区切り文字を指定します。

CSVファイルの最初の行に表示されない区切り文字以外の_nodelimには、任意の1バイト文字を使用します。 制御文字「削除」(ASCII 127) を任意に選択しています。その文字はここでSOに飲み込まれるため、代わりにchr(127)を使用して生成しますが、これも有効です。文字がポップアップしないと仮定します-または、区切り文字を4番目の関数パラメーターとして指定します。

この関数は、テーブル名とインポートされた行の数を返します。
セッションの終了時に一時テーブルが終了することを忘れないでください。

マニュアル:

PROGRAMを使用してコマンドを実行すると、SELinuxなどのオペレーティングシステムのアクセス制御メカニズムによって制限される場合があります。

SOの関連回答:

Postgres 8.4

そのバージョンは古すぎる 、ここまでバックポートするつもりはありません。

  • GET DIAGNOSTICSはオプション機能です。そのままにするか、テーブルの全数に置き換えることができます

  • 9.3ページのPROGRAMCOPY句の原始的な(高価な)代替手段は、代わりに完全なテーブルをインポートすることです。

    EXECUTE format($$COPY tmp0 FROM %L WITH (DELIMITER %L)$$, _file, _delim);
    

または、2番目の入力ファイルを準備するか、シェルからパイプすることによってそれを機能させることができます。COPY tablename FROM STDINは8.4ページで使用できます。

4

このプログラムは、ほとんどすべてのCSVまたはJSONに基づくPostgreSQLテーブルの作成を1行で処理します。

https://github.com/lukasmartinelli/pgfutter

1
CalZ