web-dev-qa-db-ja.com

Postgres:.sqlファイルの\ copy構文エラー

Postgres8.4でクロス集計クエリから.csvファイルにデータをコピーするスクリプトを作成しようとしています。 psqlコマンドラインでコマンドを実行できますが、コマンドをファイルに入れて-fオプションを使用して実行すると、構文エラーが発生します。

これが私が見ているものの例です( this 素晴らしい答えから):

CREATE TEMP TABLE t (
  section   text
 ,status    text
 ,ct        integer 
);

INSERT INTO t VALUES
 ('A', 'Active', 1), ('A', 'Inactive', 2)
,('B', 'Active', 4), ('B', 'Inactive', 5)
                   , ('C', 'Inactive', 7);

\copy (
SELECT * FROM crosstab(
       'SELECT section, status, ct
        FROM   t
        ORDER  BY 1,2' 
       ,$$VALUES ('Active'::text), ('Inactive')$$)
AS ct ("Section" text, "Active" int, "Inactive" int)
) TO 'test.csv' HEADER CSV

次にこれを実行すると、次の構文エラーが発生します。

$ psql [system specific] -f copy_test.sql
CREATE TABLE
INSERT 0 5
psql:copy_test.sql:12: \copy: parse error at end of line
psql:copy_test.sql:19: ERROR:  syntax error at or near ")"
LINE 7: ) TO 'test.csv' HEADER CSV
        ^

クロス集計なしで単純なクエリを実行する同様の演習は、問題なく機能します。

構文エラーの原因と、スクリプトファイルを使用してこのテーブルをcsvファイルにコピーするにはどうすればよいですか?

10
David Kelley

この回答 と同様に、1行の\copyコマンドで複数行のVIEWを作成します。例:

CREATE TEMP TABLE t (
  section   text
 ,status    text
 ,ct        integer 
);

INSERT INTO t VALUES
 ('A', 'Active', 1), ('A', 'Inactive', 2)
,('B', 'Active', 4), ('B', 'Inactive', 5)
                   , ('C', 'Inactive', 7);
CREATE TEMP VIEW v1 AS
  SELECT * FROM crosstab(
         'SELECT section, status, ct
          FROM   t
          ORDER  BY 1,2' 
         ,$$VALUES ('Active'::text), ('Inactive')$$)
  AS ct ("Section" text, "Active" int, "Inactive" int);

\copy (SELECT * FROM v1) TO 'test.csv' HEADER CSV

-- optional
DROP VIEW v1;
5
Mike T

psqlは、最初のコマンドは\copy (およびその下の行は別の無関係なステートメントからのものです。改行はそれらのターミネータであるため、メタコマンドは複数行に分散されません。

psql manpage からの関連する抜粋といくつかの強調が追加されました:

メタコマンド

引用符で囲まれていない円記号で始まるpsqlに入力するものはすべて、psql自体によって処理されるpsqlメタコマンドです。これらのコマンドにより、psqlは管理やスクリプト作成にさらに役立ちます。メタコマンドは、スラッシュコマンドまたはバックスラッシュコマンドと呼ばれることがよくあります。
..。
....(スキップ)

引数の解析は、行の終わりで停止するか、引用符で囲まれていない別の円記号が見つかったときに停止します。引用符で囲まれていない円記号は、新しいメタコマンドの開始と見なされます。特別なシーケンス\\(2つの円記号)は引数の終わりを示し、SQLコマンドがある場合はそれを解析し続けます。そうすれば、SQLコマンドとpsqlコマンドを1行で自由に組み合わせることができます。 ただし、いずれの場合も、メタコマンドの引数は行末を超えて続行できません。

したがって、最初のエラーは\copy (失敗した場合、以下の行は独立したSELECTとして解釈され、誤った閉じ括弧がある7行目まで問題なく表示されます。

コメントで述べられているように、修正はメタコマンド全体を1行に詰め込むことです。

12
Daniel Vérité

ここにリストされている回答は、その理由を非常に明確に説明しています。これは、SQLに複数の行を含めてpsqlを操作できるようにする小さなハックです。

# Using a file
psql -f <(tr -d '\n' < ~/s/test.sql )
# or
psql < <(tr -d '\n' < ~/s/test.sql )

# Putting the SQL using a HEREDOC
cat <<SQL | tr -d '\n'  | \psql mydatabase
\COPY (
  SELECT
    provider_id,
    provider_name,
    ...
) TO './out.tsv' WITH( DELIMITER E'\t', NULL '', )
SQL
0

psqlドキュメント によると:

-fファイル名

-ファイルファイル名

コマンドをインタラクティブに読み取る代わりに、ファイルファイル名をコマンドのソースとして使用します。ファイルが処理された後、psqlは終了します。これは多くの点で内部コマンド\ iと同等です。

ファイル名が-(ハイフン)の場合、標準入力が読み取られます。

このオプションの使用は、psql <filenameの記述とは微妙に異なります。一般に、どちらも期待どおりに機能しますが、-fを使用すると、行番号付きのエラーメッセージなどのいくつかの優れた機能が有効になります。このオプションを使用すると、起動時のオーバーヘッドが削減される可能性もわずかにあります。一方、シェルの入力リダイレクトを使用するバリアントは、(理論的には)すべてを手動で入力した場合とまったく同じ出力を生成することが保証されています。

これは、-fオプションが入力をコマンドラインとは異なる方法で処理する場合の1つです。改行の削除は機能し、元のファイルをpsqlのstdinにリダイレクトすることも機能した可能性があります。

0
Politank-Z