Wie verwendet man Skriptvariablen in psql?

133

In MS SQL Server erstelle ich meine Skripte, um anpassbare Variablen zu verwenden:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

Ich werde dann den Wert von @somevariablezur Laufzeit ändern , abhängig von dem Wert, den ich in der jeweiligen Situation haben möchte. Da es sich oben im Skript befindet, ist es leicht zu sehen und sich zu erinnern.

Wie mache ich dasselbe mit dem PostgreSQL-Client psql?

Craig Walker
quelle
5
FWIW, der Operator \ set scheint sich auf das Befehlszeilentool psql zu beziehen, nicht auf die Batch-Sprache pgsql. Ich könnte falsch liegen.
Daniel Yankowsky
1
Auf welcher Version von Postgres bist du?
Kuberchaun

Antworten:

180

Postgres-Variablen werden beispielsweise mit dem Befehl \ set erstellt ...

\set myvariable value

... und kann dann beispielsweise ersetzt werden als ...

SELECT * FROM :myvariable.table1;

... oder ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

edit: Ab psql 9.1 können Variablen in Anführungszeichen wie folgt erweitert werden:

\set myvariable value 

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

In älteren Versionen des psql-Clients:

... Wenn Sie die Variable als Wert in einer bedingten Zeichenfolgenabfrage verwenden möchten, z.

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

... dann müssen Sie die Anführungszeichen in die Variable selbst aufnehmen, da dies nicht funktioniert. Definieren Sie stattdessen Ihre Variable als solche ...

\set myvariable 'value'

Wenn Sie jedoch wie ich auf eine Situation gestoßen sind, in der Sie aus einer vorhandenen Variablen eine Zeichenfolge erstellen möchten, habe ich den Trick als ...

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

Jetzt haben Sie sowohl eine zitierte als auch eine nicht zitierte Variable derselben Zeichenfolge! Und so etwas kannst du machen ...

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
Crowmagnumb
quelle
65
\setist nur für psqlWerkzeug, Sie können es nicht in gespeicherten Prozeduren verwenden!
Sorin
6
@ SorinSbarnea das OP fragte nach Skript , nicht Verfahren
Daniel Serodio
35
Diese Antwort mischt psqlMeta- \setBefehle auf verwirrende Weise mit PostgreSQL-Befehlen.
Erwin Brandstetter
20
Ab Postgresql 9.1 können Sie in psql jetzt Folgendes verwenden: 'variable', um es richtig als Wert für Sie in Anführungszeichen zu setzen, oder: "variable", um es als Bezeichner zu verwenden.
HitScan
9
\set myvariable 'value'ist nicht eine beliebige Zitat in der Variable, im Gegensatz zu dem, was diese Antwort sagt. Verwenden Sie im Zweifelsfall \echo :myvariablein psql, um den Wert unabhängig von einer Abfrage anzuzeigen.
Daniel Vérité
61

Ein letztes Wort zu PSQL-Variablen:

  1. Sie werden nicht erweitert, wenn Sie sie in einfache Anführungszeichen in die SQL-Anweisung setzen. Das funktioniert also nicht:

    SELECT * FROM foo WHERE bar = ':myvariable'
  2. Um in einer SQL-Anweisung zu einem Zeichenfolgenliteral zu erweitern, müssen Sie die Anführungszeichen in den Variablensatz aufnehmen. Der Variablenwert muss jedoch bereits in Anführungszeichen gesetzt werden. Dies bedeutet, dass Sie einen zweiten Satz von Anführungszeichen benötigen und der innere Satz maskiert werden muss. Sie brauchen also:

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

    BEARBEITEN : Ab PostgreSQL 9.1 können Sie stattdessen schreiben:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
Craig Walker
quelle
12
Prost für:'myvariable'
Andomar
Genau das, wonach ich gesucht habe!
KeniSteward
47

Sie können versuchen, eine WITH- Klausel zu verwenden.

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
quelle
Diese Methode ist meistens praktisch, wenn Sie in Ihrer Abfrage einige Male dieselben berechneten Werte verwenden.
Skaurus
2
Im Gegensatz zu Bryces Bericht scheint es für mich gut zu funktionieren. 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; Wie erwartet Jack und Jacks Alter.
Joshua
2
Für viele Anwendungen, insbesondere im Kontext eines Webanwendungsframeworks wie Python Flask, ist dies die beste Lösung für die Wiederverwendung komplexer berechneter Werte in einer einzelnen Abfrage.
Will
1
Kann jemand vorschlagen, wie dies in einer Beilage funktionieren könnte?
Stoopkid
@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

Speziell für psqlkönnen Sie psqlVariablen auch über die Befehlszeile übergeben. Sie können sie mit übergeben -v. Hier ist ein Anwendungsbeispiel:

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

Beachten Sie, dass der Doppelpunkt nicht in Anführungszeichen gesetzt ist und der Variablenname selbst in Anführungszeichen gesetzt wird. Seltsame Syntax, ich weiß. Dies funktioniert nur in psql; es wird in (sagen wir) PgAdmin-III nicht funktionieren.

Diese Ersetzung erfolgt während der Eingabeverarbeitung in psql. Sie können also keine Funktion definieren, die verwendet :'filepath'und erwartet, dass sich der Wert von :'filepath'von Sitzung zu Sitzung ändert. Es wird einmal ersetzt, wenn die Funktion definiert ist, und ist danach eine Konstante. Es ist nützlich für Skripte, aber nicht zur Laufzeit.

Craig Ringer
quelle
psql-Variablen, zB: 'Dateipfad', haben Sie darauf hingewiesen: "Beachten Sie, dass der Doppelpunkt nicht in Anführungszeichen gesetzt ist und der Variablenname selbst in Anführungszeichen gesetzt wird." Danken! Du! Ich habe bereits ein paar stirnförmige Dellen in meinen Schreibtisch gesteckt, um diese Arbeit zu machen, und Sie haben mir nur eine Tonne mehr gespart. Genau das, was ich für ein paar Skripte brauchte.
Jason
13

FWIW, das eigentliche Problem war, dass ich am Ende meines Befehls \ set ein Semikolon eingefügt hatte:

\ set owner_password 'thepassword';

Das Semikolon wurde als tatsächliches Zeichen in der Variablen interpretiert:

\ echo: owner_password thepassword;

Als ich versuchte, es zu benutzen:

CREATE ROLE Myrole LOGIN UNENCRYPTED PASSWORD: owner_password NOINHERIT CREATEDB CREATEROLE GÜLTIG BIS 'unendlich';

...Ich schaff das:

CREATE ROLE Myrole LOGIN UNENCRYPTED PASSWORD thepassword; NOINHERIT CREATEDB CREATEROLE GÜLTIG BIS 'unendlich';

Dadurch konnten nicht nur die Anführungszeichen um das Literal gesetzt werden, sondern der Befehl wurde auch in zwei Teile aufgeteilt (von denen der zweite ungültig war, da er mit "NOINHERIT" begann).

Die Moral dieser Geschichte: PostgreSQL "Variablen" sind wirklich Makros, die bei der Texterweiterung verwendet werden, keine wahren Werte. Ich bin mir sicher, dass das nützlich ist, aber es ist zunächst schwierig.

Craig Walker
quelle
12

Sie müssen eine der prozeduralen Sprachen wie PL / pgSQL verwenden, nicht die SQL-Proc-Sprache. In PL / pgSQL können Sie vars direkt in SQL-Anweisungen verwenden. Für einfache Anführungszeichen können Sie die Anführungszeichenliteralfunktion verwenden.


quelle
5
Dies kann nicht in Postgres selbst, sondern in der PSQL-Client-Anwendung erfolgen.
Philluminati
1
plpgsql kann (jetzt) ​​in postgres (seit Version 9.0) verwendet werden) postgresql.org/docs/9.0/static/sql-do.html
Jasen
11

postgres (seit Version 9.0) erlaubt anonyme Blöcke in einer der unterstützten serverseitigen Skriptsprachen

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

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

Da sich alles in einer Zeichenfolge befindet, müssen externe Zeichenfolgenvariablen, die ersetzt werden, maskiert und zweimal in Anführungszeichen gesetzt werden. Die Verwendung von Dollar-Anführungszeichen bietet keinen vollständigen Schutz gegen SQL-Injection.

Jasen
quelle
5

Ein anderer Ansatz besteht darin, den PostgreSQL GUC-Mechanismus (ab) zum Erstellen von Variablen zu verwenden. Siehe diese vorherige AntwortEinzelheiten und Beispiele finden .

Sie deklarieren den GUC in postgresql.conf, ändern dann seinen Wert zur Laufzeit mit SETBefehlen und erhalten seinen Wert mitcurrent_setting(...) .

Ich empfehle dies nicht für den allgemeinen Gebrauch, aber es könnte in engen Fällen wie dem in der verknüpften Frage erwähnten nützlich sein, in denen das Poster eine Möglichkeit wollte, den Benutzernamen auf Anwendungsebene für Trigger und Funktionen bereitzustellen.

Craig Ringer
quelle
4

Ich habe es mit einem temporären Tisch gelöst.

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

Auf diese Weise hatte ich eine "Variable", die ich für mehrere Abfragen verwenden konnte und die für die Sitzung eindeutig ist. Ich brauchte es, um eindeutige "Benutzernamen" zu generieren, ohne Kollisionen zu haben, wenn Benutzer mit demselben Benutzernamen importiert wurden.

Geon
quelle
Dies scheint die einzige Arbeitsweise in visuellen Tools wie Heidi SQL zu sein.
Altair7852
2

Ich fand diese Frage und die Antworten äußerst nützlich, aber auch verwirrend. Ich hatte viele Probleme damit, zitierte Variablen zum Laufen zu bringen. Deshalb habe ich es so zum Laufen gebracht:

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

Auf diese Weise können Sie die Variable in einer Anweisung definieren. Wenn Sie es verwenden, werden einfache Anführungszeichen in die Variable eingebettet.

HINWEIS! Wenn ich nach der zitierten Variablen einen Kommentar einfügte, wurde dieser als Teil der Variablen aufgenommen, als ich einige der Methoden in anderen Antworten ausprobierte. Das hat mich eine Weile wirklich fertig gemacht. Mit dieser Methode werden Kommentare wie erwartet behandelt.

Nate
quelle
\set deployment_pass 'string_password' ALTER USER :deployment_user WITH PASSWORD :'deployment_pass';
Jasen
\ set ist kein SQL, sondern ein in psql integrierter Befehl. SQL-Kommentare werden nicht unterstützt.
Jasen
2

Ich vermisse diese Funktion wirklich. Eine ähnliche Möglichkeit besteht nur darin, Funktionen zu verwenden.

Ich habe es auf zwei Arten benutzt:

  • Perl-Funktionen, die die Variable $ _SHARED verwenden
  • Speichern Sie Ihre Variablen in der Tabelle

Perl-Version:

   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;

Tabellenversion:

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';

Anmerkungen:

  • Plperlu ist schneller als Perl
  • pg_backend_pid ist nicht die beste Sitzungsidentifikation. Verwenden Sie pid in Kombination mit backend_start von pg_stat_activity
  • Diese Tabellenversion ist auch schlecht, weil Sie löschen müssen, dass dies gelegentlich aktiv ist (und derzeit arbeitende Sitzungsvariablen nicht löschen).
Kaiko Kaur
quelle
1

Variablen in psqlsaugen. Wenn Sie eine Ganzzahl deklarieren möchten, müssen Sie die Ganzzahl eingeben, einen Wagenrücklauf durchführen und die Anweisung in einem Semikolon beenden. Beobachten:

Angenommen, ich möchte eine Ganzzahlvariable deklarieren my_varund in eine Tabelle einfügen test:

Beispieltabelle 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)

Klar, noch nichts in dieser Tabelle:

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

Wir deklarieren eine Variable. Beachten Sie, wie das Semikolon in der nächsten Zeile steht!

thedatabase=# \set my_var 999
thedatabase=# ;

Jetzt können wir einfügen. Wir müssen diese seltsam :''aussehende Syntax verwenden:

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

Es funktionierte!

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

Erläuterung:

Also ... was passiert, wenn wir das Semikolon in der nächsten Zeile nicht haben? Die Variable? Guck mal:

Wir erklären my_varohne die neue Zeile.

thedatabase=# \set my_var 999;

Lassen Sie uns auswählen my_var.

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

Was zum Teufel ist das? Es ist keine ganze Zahl , es ist eine Zeichenfolge 999; !

thedatabase=# select 999;
 ?column? 
----------
      999
(1 row)
Anzeigename
quelle
5
Der Grund, warum das Semikolon unerwartete Dinge für Sie tut, ist, dass ein Semikolon eine SQL-Anweisung beendet, Sie jedoch den Befehl psql \ set eingeben, der kein SQL ist und KEIN Abschlusssemikolon benötigt. Das Setzen eines Semikolons in die nächste Zeile tut nicht weh, tut aber absolut nichts. Es ist eine leere Aussage.
volkerk
1

Ich habe eine neue Lösung dafür in einem anderen Thread veröffentlicht .

Es verwendet eine Tabelle zum Speichern von Variablen und kann jederzeit aktualisiert werden. Eine statische unveränderliche Getter-Funktion wird dynamisch (von einer anderen Funktion) erstellt und durch Aktualisierung Ihrer Tabelle ausgelöst. Sie erhalten eine schöne Tischaufbewahrung sowie die blitzschnellen Geschwindigkeiten eines unveränderlichen Getters.

Brev
quelle