So deklarieren Sie eine Variable in einer PostgreSQL-Abfrage

240

Wie deklariere ich eine Variable zur Verwendung in einer PostgreSQL 8.3-Abfrage?

In MS SQL Server kann ich Folgendes tun:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Wie mache ich dasselbe in PostgreSQL? Laut Dokumentation werden Variablen einfach als "Namenstyp" deklariert, aber dies gibt mir einen Syntaxfehler:

myvar INTEGER;

Könnte mir jemand ein Beispiel für die richtige Syntax geben?

EMP
quelle
2
Dies kann nur in PostgreSQL erfolgen. Siehe die Antwort auf diese verwandte Frage: stackoverflow.com/questions/766657/…
Sean the Bean
2
Diese verwandte Antwort hat bessere Antworten: stackoverflow.com/questions/13316773/…
Erwin Brandstetter

Antworten:

113

In PostgreSQL gibt es keine solche Funktion. Sie können dies nur in pl / PgSQL (oder einem anderen pl / *) tun, nicht jedoch in einfachem SQL.

Eine Ausnahme ist die WITH ()Abfrage, die als Variable oder sogar tuplevon Variablen verwendet werden kann. Sie können eine Tabelle mit temporären Werten zurückgeben.

WITH master_user AS (
    SELECT
      login,
      registration_date
    FROM users
    WHERE ...
)

SELECT *
FROM users
WHERE master_login = (SELECT login
                      FROM master_user)
      AND (SELECT registration_date
           FROM master_user) > ...;
J. Princewicz
quelle
Ich habe diese Methode ausprobiert, bei der CTEs als Variablen verwendet werden. Aber dann stieß ich schnell auf ein Problem, bei dem verschiedene Datenänderungsabfragen in CTEs nicht garantiert sind, dass sie sich gegenseitig beeinflussen. Ich musste mehrere CTEs verwenden, da ich diese Variable in mehreren Abfragen verwenden musste.
Zia Ul Rehman Mughal
227

Ich habe das gleiche Ziel mit einer WITHKlausel erreicht , sie ist bei weitem nicht so elegant, kann aber das Gleiche tun. Obwohl es für dieses Beispiel wirklich übertrieben ist. Ich empfehle dies auch nicht besonders.

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;
fei0x
quelle
2
Dies funktioniert hervorragend in den meisten Fällen, in denen Sie Variablen wünschen. Wenn Sie jedoch eine Variable für LIMIT verwenden möchten (die keine Variablen enthalten kann), möchten Sie diese \setwie in Shahriar Aghajanis Antwort vorgeschlagen verwenden.
Cimmanon
1
Dies ist ideal, wenn ich ein Migrationsskript habe, in das ich einige relationale Daten importieren möchte. Offensichtlich weiß ich nicht, welche Sequenz-ID die relationalen Daten enthalten.
Relequestual
3
Ich habe gerade diesen Ansatz ausprobiert und einen vielleicht besseren Weg gefunden: JOIN myconstants ON trueund dann besteht keine Notwendigkeit, die Unterauswahl vorzunehmen.
Vektor
7
Dies funktioniert nur innerhalb einer einzelnen Abfrage. Sie können einen WITHCTE nicht für Abfragen in einer Transaktion freigeben.
Daenyth
2
Alte Frage, aber hier ist eine Variation : WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. CROSS JOIN ist ein einzeiliger Tabellenausdruck, der die Daten für alle Zeilen in der realen Tabelle virtuell dupliziert und den Ausdruck vereinfacht.
Manngo
82

Sie können dies auch in PLPGSQL versuchen:

DO $$
DECLARE myvar integer;
BEGIN
    SELECT 5 INTO myvar;

    DROP TABLE IF EXISTS tmp_table;
    CREATE TABLE tmp_table AS
    SELECT * FROM yourtable WHERE   id = myvar;
END $$;

SELECT * FROM tmp_table;

Für das oben Gesagte ist Postgres 9.0 oder höher erforderlich.

Dario Barrionuevo
quelle
1
Die DO-Anweisung wurde in PostgreSQL 9.0 hinzugefügt und funktioniert in 8.3 nicht.
Johny
14
Verwenden Sie CREATE TEMPORARY TABLE oder CREATE TEMP TABLE, nicht CREATE TABLE. Aber sonst gut.
Stefan Steiger
60

Dynamische Konfigurationseinstellungen

Sie können dynamische Konfigurationseinstellungen dafür "missbrauchen":

-- choose some prefix that is unlikely to be used by postgres
set session my.vars.id = '1';

select *
from person 
where id = current_setting('my.vars.id')::int;

Konfigurationseinstellungen sind immer Varchar-Werte, daher müssen Sie sie bei Verwendung auf den richtigen Datentyp umwandeln. Dies funktioniert mit jedem SQL-Client, während \setnur inpsql

Für das oben Gesagte ist Postgres 9.2 oder höher erforderlich.

Bei früheren Versionen musste die Variable postgresql.confvor der Verwendung deklariert werden, sodass ihre Verwendbarkeit etwas eingeschränkt wurde. Eigentlich nicht die Variable komplett, sondern die config "Klasse", die im Wesentlichen das Präfix ist. Sobald das Präfix definiert wurde, kann jede Variable ohne Änderung verwendet werdenpostgresql.conf

ein Pferd ohne Name
quelle
3
@BrijanElwadhi: Ja, das ist eine Transaktion.
a_horse_with_no_name
2
Als Randnotiz: Einige Wörter sind reserviert, zum Beispiel wird ein Wechsel set session my.vars.id = '1';zu set session my.user.id = '1';ergebenERROR: syntax error at or near "user"
Dominik
2
@BrijanElwadhi: Um variable Transaktionen spezifisch zu machen, müssen Sie Folgendes verwenden : SET LOCAL .... Die sessionVariable bleibt so lange gültig, wie Ihre Verbindung besteht. Das localist auf die Transaktion beschränkt.
Eugen Konkov
@dominik Sie können diese Einschränkung mit Anführungszeichen umgehen, z. B. set session "my.user.id" = '1';Der current_setting('my.user.id')Aufruf funktioniert wie erwartet.
Miles Elam
58

Das hängt von Ihrem Kunden ab.

Wenn Sie jedoch den psql- Client verwenden, können Sie Folgendes verwenden:

my_db=> \set myvar 5
my_db=> SELECT :myvar  + 1 AS my_var_plus_1;
 my_var_plus_1 
---------------
             6

Wenn Sie Textvariablen verwenden, müssen Sie Anführungszeichen setzen.

\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';
Shahriar Aghajani
quelle
1
\setmuss klein geschrieben sein
deluan
db = # \ set profile_id 102 db = #: profile_id; FEHLER: Syntaxfehler bei oder nahe "102" LINE 1: 102; ^
AlxVallejo
1
@AlxVallejo müssen Sie es in Anweisung und psql- Konsole verwenden. db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
Everis
21

Verwenden einer Temp-Tabelle außerhalb von pl / PgSQL

Abgesehen von der vorgeschlagenen Verwendung von pl / pgsql oder einer anderen pl / * -Sprache ist dies die einzige andere Möglichkeit, die mir in den Sinn kommt.

begin;
select 5::int as var into temp table myvar;
select *
  from somewhere s, myvar v
 where s.something = v.var;
commit;
Evan Carroll
quelle
13

Ich möchte eine Verbesserung der Antwort von @ DarioBarrionuevo vorschlagen , um die Nutzung temporärer Tabellen zu vereinfachen.

DO $$
    DECLARE myvar integer = 5;
BEGIN
    CREATE TEMP TABLE tmp_table ON COMMIT DROP AS
        -- put here your query with variables:
        SELECT * 
        FROM yourtable
        WHERE id = myvar;
END $$;

SELECT * FROM tmp_table;
bläulich
quelle
Eine gute Lösung zum Lösen des DO-Blocks kann keinen Datensatz zurückgeben!
CodeFarmer
Unter PostgreSQL 11.0 gibt eine solche Abfrage 1(vermutlich die Zeilenanzahl) und nicht den Inhalt von zurück tmp_table.
Ed Noepel
9

Diese Lösung basiert auf der von fei0x vorgeschlagenen , hat jedoch den Vorteil, dass die Werteliste der Konstanten in der Abfrage nicht verknüpft werden muss und Konstanten zu Beginn der Abfrage einfach aufgelistet werden können. Es funktioniert auch in rekursiven Abfragen.

Grundsätzlich ist jede Konstante eine Einzelwerttabelle , die in einer WITH-Klausel deklariert ist und dann an einer beliebigen Stelle im verbleibenden Teil der Abfrage aufgerufen werden kann.

  • Grundlegendes Beispiel mit zwei Konstanten:
WITH
    constant_1_str AS (VALUES ('Hello World')),
    constant_2_int AS (VALUES (100))
SELECT *
FROM some_table
WHERE table_column = (table constant_1_str)
LIMIT (table constant_2_int)

Alternativ können Sie SELECT * FROM constant_namestattdessen verwenden, TABLE constant_namewas möglicherweise nicht für andere Abfragesprachen gültig ist, die sich von postgresql unterscheiden.

Jorge Luis
quelle
6

Hier ist ein Beispiel mit PREPARE-Anweisungen . Sie können immer noch nicht verwenden ?, aber Sie können $nNotation verwenden:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;
Martin Zinovsky
quelle
Funktioniert ganz gut! Vielen Dank.
Rui Carvalho
4

Es gibt zwar keine anschauliche und eindeutige Möglichkeit, eine einwertige Variable zu deklarieren. Sie können dies jedoch tun

with myVar as (select "any value really")

Um dann Zugriff auf den in dieser Konstruktion gespeicherten Wert zu erhalten, müssen Sie dies tun

(select * from myVar)

beispielsweise

with var as (select 123)    
... where id = (select * from var)
Michael.Medvedskiy
quelle
3

Sie können auf spezielle Werkzeugfunktionen zurückgreifen. Wie für DBeaver eigene proprietäre Syntax:

@set name = 'me'
SELECT :name;
SELECT ${name};

DELETE FROM book b
WHERE b.author_id IN (SELECT a.id FROM author AS a WHERE a.name = :name);
Gavenkoa
quelle
Dies ist näher an der Verwendbarkeit: Ich werde untersuchen, ob DBeaver Listen und Schleifen unterstützt: Ich muss dieselbe SQL auf mehrere Schemas anwenden, und die Liste würde die Schemas enthalten, auf die sie angewendet werden sollen.
Javadba
1

In DBeaver können Sie Parameter in Abfragen genau wie aus Code verwenden. Dies funktioniert also:

SELECT *
FROM somewhere
WHERE something = :myvar

Wenn Sie die Abfrage ausführen, werden Sie von DBeaver nach dem Wert für: myvar gefragt und die Abfrage ausgeführt.

Der Codierer
quelle