psqlでスクリプト変数をどのように使用しますか?


133

MS SQL Serverで、カスタマイズ可能な変数を使用するスクリプトを作成します。

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

次に@somevariable、特定の状況で必要な値に応じて、実行時にの値を変更します。スクリプトの上部にあるので、見やすく、覚えやすいです。

PostgreSQLクライアントで同じことをするにはどうすればよいpsqlですか?


5
FWIW、\ set演算子、pgsqlバッチ言語ではなく、psqlコマンドラインツールに関連しているようです。私は間違っている可能性があります。
Daniel Yankowsky、2011年

1
Postgresのどのバージョンを使用していますか?
Kuberchaun

回答:


180

Postgres変数は、たとえば\ setコマンドを使用して作成されます...

\set myvariable value

...そして、たとえば...のように置き換えることができます。

SELECT * FROM :myvariable.table1;

...または...

SELECT * FROM table1 WHERE :myvariable IS NULL;

編集:psql 9.1以降、変数は次のように引用符で拡張できます。

\set myvariable value 

SELECT * FROM table1 WHERE column1 = :'myvariable';

古いバージョンのpsqlクライアントでは:

...変数を条件付き文字列クエリの値として使用したい場合、例えば...

SELECT * FROM table1 WHERE column1 = ':myvariable';

...次に、上記は機能しないため、変数自体に引用符を含める必要があります。代わりに、そのように変数を定義します...

\set myvariable 'value'

しかし、私のように、既存の変数から文字列を作成したい状況に遭遇した場合、私はこれがトリックであることを発見しました...

\set quoted_myvariable '\'' :myvariable '\''

これで、同じ文字列の引用符付き変数と引用符なし変数の両方ができました。そして、あなたはこのようなことをすることができます...

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;

66
\setpsqlツール専用であり、ストアドプロシージャでは使用できません。
ソリン

6
@SorinSbarnea OPは、手順ではなくスクリプトについて質問しました
Daniel Serodio

35
この回答では、psqlメタコマンド\setとPostgreSQLコマンドが混同しています。
Erwin Brandstetter 2013年

20
postgresql 9.1以降、psqlで: 'variable'を使用して値として適切に引用符で囲むか、または: "variable"を識別子として使用できます。
HitScan 2013年

9
\set myvariable 'value'この答えが言うことに反して、変数内に引用符含まれませ。疑わしい場合は、\echo :myvariablepsql内で使用して、クエリとは無関係に値を表示します。
DanielVérité2014年

61

PSQL変数に関する最後の言葉:

  1. SQLステートメントで一重引用符で囲むと、展開されません。したがって、これは機能しません:

    SELECT * FROM foo WHERE bar = ':myvariable'
  2. SQLステートメントで文字列リテラルに展開するには、変数セットに引用符を含める必要があります。ただし、変数値はすでに引用符で囲む必要があります。つまり、2番目の引用符のセットが必要であり、内部セットはエスケープする必要があります。したがって、次のものが必要です。

    \set myvariable '\'somestring\''  
    SELECT * FROM foo WHERE bar = :myvariable

    編集:PostgreSQL 9.1以降、代わりに次のように書くことができます:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'

12
乾杯:'myvariable'
Andomar 2018年

まさに私が探していたもの!
KeniSteward

47

WITH句を使用してみることができます。

WITH vars AS (SELECT 42 AS answer, 3.14 AS appr_pi)
SELECT t.*, vars.answer, t.radius*vars.appr_pi
FROM table AS t, vars;

この方法は、クエリで同じ計算値を数回使用する場合に最も便利です。
skaurus 2013年

2
ブライスのレポートに反して、それは私にとってはうまくいくようです。 CREATE TABLE test (name VARCHAR, age INT); INSERT INTO test (name, age) VALUES ('Jack', 21), ('Jill', 20); WITH vars AS (SELECT N'Jack' AS name, 21 AS age) SELECT test.* FROM test, vars WHERE test.name = vars.name and test.age = vars.age; 予想どおり、ジャックとジャックの年齢です。
ジョシュア

2
多くの用途で、特にPython FlaskなどのWebアプリケーションフレームワークのコンテキストでは、これは単一のクエリ内で複雑な計算値を再利用するための最良のソリューションです。
ウィル

1
これがインサートでどのように機能するかを誰かが提案できますか?
Stoopkid 2017

@Stoopkidcreate table t(x integer); insert into t(x) with sub as (select 999 as num) select num from sub; select * from t;
JL_SO

33

具体的にはpsqlpsqlコマンドラインから変数を渡すこともできます。あなたはそれらを渡すことができます-v。次に使用例を示します。

$ psql -v filepath=/path/to/my/directory/mydatafile.data regress
regress=> SELECT :'filepath';
               ?column?                
---------------------------------------
 /path/to/my/directory/mydatafile.data
(1 row)

コロンは引用符で囲まれず、変数名自体が引用符で囲まれていることに注意してください。奇妙な構文、私は知っています。これはpsqlでのみ機能します。(たとえば)PgAdmin-IIIでは機能しません。

この置換はpsqlでの入力処理中に発生するため、値を使用してセッションごとに変化:'filepath'することを期待する関数を(たとえば)定義することはできません:'filepath'。これは、関数が定義されたときに一度置換され、その後定数になります。スクリプト作成には役立ちますが、ランタイムには使用できません。


: 'filepath'などのpsql変数は、次のように指摘しています。「コロンは引用符で囲まず、変数名自体が引用符で囲まれていることに注意してください。」感謝!君は!私はすでにこの額の形のへこみの束を机に入れてこの仕事を作ろうとしていました。スクリプトに必要なもの。
Jason

13

FWIW、本当の問題は、\ setコマンドの最後にセミコロンを含めたことです。

\ set owner_password 'thepassword';

セミコロンは変数内の実際の文字として解釈されました:

\ echo:owner_password thepassword;

だから私がそれを使おうとしたとき:

CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD:owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';

...私はこれを得た:

CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD thepassword; NOINHERIT CREATEDB CREATEROLEは '無限'まで有効です。

これは、リテラルを囲む引用符の設定に失敗しただけでなく、コマンドを2つの部分に分割しました(2番目の部分は、 "NOINHERIT"で始まったため無効でした)。

この話の教訓:PostgreSQLの「変数」は、実際にはテキスト展開で使用されるマクロであり、真の値ではありません。これは便利だと思いますが、最初は注意が必要です。


12

SQL proc言語ではなく、PL / pgSQLなどのいずれかの手続き型言語を使用する必要があります。PL / pgSQLでは、SQLステートメントで直接varsを使用できます。単一引用符の場合は、引用リテラル関数を使用できます。


5
postgres自体では実行できませんが、PSQLクライアントアプリケーションでは実行できます。
Philluminati 2011

1
plpgsqlは(現在)postgres(バージョン9.0以降)で使用できます)postgresql.org/docs/9.0/static/sql-do.html
Jasen

11

postgres(バージョン9.0以降)は、サポートされている任意のサーバー側スクリプト言語で匿名ブロックを許可します

DO '
DECLARE somevariable int = -1;
BEGIN
INSERT INTO foo VALUES ( somevariable );
END
' ;

http://www.postgresql.org/docs/current/static/sql-do.html

すべてが文字列内にあるため、置換される外部文字列変数はエスケープして2回引用する必要があります。代わりにドル引用を使用しても、SQLインジェクションに対する完全な保護は得られません。


5

別のアプローチは、PostgreSQL GUCメカニズムを(ab)使用して変数を作成することです。参照してください。この前の答え詳細と例については、を。

でGUCを宣言し、コマンドpostgresql.confでその値を実行時に変更し、でその値をSET取得しますcurrent_setting(...)

これは一般的な使用にはお勧めしませんが、リンクされた質問で言及されているような、ポスターやトリガーや関数にアプリケーションレベルのユーザー名を提供する方法が必要であるような狭いケースでは役立ちます。


4

一時テーブルで解決しました。

CREATE TEMP TABLE temp_session_variables (
    "sessionSalt" TEXT
);
INSERT INTO temp_session_variables ("sessionSalt") VALUES (current_timestamp || RANDOM()::TEXT);

このように、複数のクエリで使用できる「変数」がありました。これは、セッションに固有のものです。同じユーザー名のユーザーをインポートする場合に衝突を起こさずに、一意の「ユーザー名」を生成するために必要でした。


これは、Heidi SQLのようなビジュアルツールで唯一有効な方法のようです。
Altair7852

2

この質問とその回答は非常に役に立ちましたが、混乱もしました。クォートされた変数を機能させるのに多くの問題があったので、ここにそれを機能させる方法があります:

\set deployment_user username    -- username
\set deployment_pass '\'string_password\''
ALTER USER :deployment_user WITH PASSWORD :deployment_pass;

このようにして、1つのステートメントで変数を定義できます。使用すると、単一引用符が変数に埋め込まれます。

注意!引用された変数の後にコメントを付けると、他の回答でいくつかのメソッドを試したときに、変数の一部として吸い込まれました。それは本当に私をしばらく混乱させていました。この方法では、コメントは期待どおりに扱われるように見えます。


\set deployment_pass 'string_password' ALTER USER :deployment_user WITH PASSWORD :'deployment_pass';
Jasen、2016年

\ setはSQLではなく、psql組み込みコマンドです。sqlコメントはサポートされていません。
Jasen 2016年

2

私はその機能が本当に恋しいです。同様のことを達成する唯一の方法は、関数を使用することです。

私はそれを2つの方法で使用しました:

  • $ _SHARED変数を使用するperl関数
  • 変数をテーブルに保存する

Perlバージョン:

   CREATE FUNCTION var(name text, val text) RETURNS void AS $$
        $_SHARED{$_[0]} = $_[1];
   $$ LANGUAGE plperl;
   CREATE FUNCTION var(name text) RETURNS text AS $$
        return $_SHARED{$_[0]};
   $$ LANGUAGE plperl;

テーブルバージョン:

CREATE TABLE var (
  sess bigint NOT NULL,
  key varchar NOT NULL,
  val varchar,
  CONSTRAINT var_pkey PRIMARY KEY (sess, key)
);
CREATE FUNCTION var(key varchar, val anyelement) RETURNS void AS $$
  DELETE FROM var WHERE sess = pg_backend_pid() AND key = $1;
  INSERT INTO var (sess, key, val) VALUES (sessid(), $1, $2::varchar);
$$ LANGUAGE 'sql';

CREATE FUNCTION var(varname varchar) RETURNS varchar AS $$
  SELECT val FROM var WHERE sess = pg_backend_pid() AND key = $1;
$$ LANGUAGE 'sql';

ノート:

  • plperluはperlより速い
  • pg_backend_pidは最良のセッション識別ではありません。pidをpg_stat_activityのbackend_startと組み合わせて使用​​することを検討してください
  • このテーブルのバージョンは、これが時々アップしていることをクリアする必要があるため(そして現在動作しているセッション変数を削除しないため)、悪いことです。

1

psqlサックの変数。整数を宣言する場合は、整数を入力してから改行し、ステートメントをセミコロンで終了する必要があります。観察する:

整数変数を宣言my_varしてテーブルに挿入するとしますtest

テーブルの例test

thedatabase=# \d test;
                         Table "public.test"
 Column |  Type   |                     Modifiers                     
--------+---------+---------------------------------------------------
 id     | integer | not null default nextval('test_id_seq'::regclass)
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)

明らかに、この表にはまだ何もありません。

thedatabase=# select * from test;
 id 
----
(0 rows)

変数を宣言します。セミコロンが次の行にあることに注意してください!

thedatabase=# \set my_var 999
thedatabase=# ;

これで挿入できます。この奇妙な " :''"構文を使用する必要があります。

thedatabase=# insert into test(id) values (:'my_var');
INSERT 0 1

出来た!

thedatabase=# select * from test;
 id  
-----
 999
(1 row)

説明:

では、次の行にセミコロンがないとどうなりますか?変数?見てください:

my_var改行なしで宣言します。

thedatabase=# \set my_var 999;

選択してみましょうmy_var

thedatabase=# select :'my_var';
 ?column? 
----------
 999;
(1 row)

あれは何だよ?それは整数ではなく、文字列 999;です!

thedatabase=# select 999;
 ?column? 
----------
      999
(1 row)

5
セミコロンが予期しない動作をする理由は、セミコロンがSQLステートメントを終了するが、SQLではないpsqlコマンド\ setを入力しているため、終了セミコロンを使用しないためです。セミコロンを次の行に置いても害はありませんが、まったく何もしません。空のステートメントです。
volkerk

1

これに関する新しい解決策を別のスレッドに投稿しました。

テーブルを使用して変数を格納し、いつでも更新できます。静的で不変のゲッター関数は、別の関数によって動的に作成され、テーブルの更新によってトリガーされます。素晴らしいテーブルストレージに加えて、不変のゲッターの非常に速い速度が得られます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.