Come dichiarare una variabile in una query PostgreSQL


242

Come posso dichiarare una variabile da utilizzare in una query PostgreSQL 8.3?

In MS SQL Server posso fare questo:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Come faccio lo stesso in PostgreSQL? Secondo la documentazione le variabili sono dichiarate semplicemente come "tipo di nome;", ma questo mi dà un errore di sintassi:

myvar INTEGER;

Qualcuno potrebbe darmi un esempio della sintassi corretta?


2
Può essere fatto solo in PostgreSQL. Vedi la risposta a questa domanda correlata: stackoverflow.com/questions/766657/…
Sean the Bean,

2
Questa risposta correlata ha risposte migliori: stackoverflow.com/questions/13316773/…
Erwin Brandstetter,

Risposte:


113

PostgreSQL non dispone di tale funzionalità. Puoi farlo solo in pl / PgSQL (o altro pl / *), ma non in SQL semplice.

Un'eccezione è la WITH ()query che può funzionare come una variabile o anche come tuplevariabili. Ti consente di restituire una tabella di valori temporanei.

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) > ...;

Ho provato questo metodo di CTE usato come variabile. Ma poi mi sono imbattuto rapidamente in un problema in cui diverse query di modifica dei dati nei CTE non sono garantite per vedere gli effetti reciproci. Ho dovuto usare più CTE di cui avevo bisogno per usare quella variabile in più query.
Zia Ul Rehman Mughal,

228

Ho raggiunto lo stesso obiettivo usando una WITHclausola , non è affatto elegante ma può fare la stessa cosa. Anche se per questo esempio è davvero eccessivo. Inoltre non lo consiglio particolarmente.

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;

2
Funziona benissimo per la maggior parte dei casi in cui vorresti variabili. Tuttavia, se si desidera utilizzare una variabile per LIMIT (che non può contenere variabili), è \setconsigliabile utilizzare come suggerito nella risposta di Shahriar Aghajani.
cimmanon,

1
Questo è l'ideale per quando ho uno script di migrazione in cui voglio importare alcuni dati relazionali. Ovviamente non saprò l'ID sequenza fornito dai dati relazionali.
Relequestual

3
Ho appena provato questo approccio e ho trovato un modo forse migliore: JOIN myconstants ON truee quindi non è necessario effettuare la sottoselezione.
Vektor,

7
Funziona solo all'interno di una singola query, non è possibile condividere un WITHCTE tra query in una transazione.
Daenyth,

2
Vecchia questione, ma ecco qui è una variante: WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. CROSS JOIN, essendo un con un'espressione di tabella a riga singola, duplica virtualmente i dati per tutte le righe della tabella reale e semplifica l'espressione.
Manngo,

83

Puoi anche provare questo in PLPGSQL:

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;

Quanto sopra richiede Postgres 9.0 o successivo.


1
L'istruzione DO è stata aggiunta in PostgreSQL 9.0 e non funziona in 8.3.
Johny,

14
Utilizzare CREATE TAVOLA TEMPORANEA o CREATE TABELLA TEMP, non CREATE TABLE. Ma per il resto va bene.
Stefan Steiger,

60

Impostazioni di configurazione dinamica

puoi "abusare" delle impostazioni di configurazione dinamica per questo:

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

Le impostazioni di configurazione sono sempre valori varchar, quindi è necessario trasmetterle al tipo di dati corretto quando le si utilizza. Funziona con qualsiasi client SQL mentre \setfunziona solo conpsql

Quanto sopra richiede Postgres 9.2 o successivo.

Per le versioni precedenti, la variabile doveva essere dichiarata postgresql.confprima di essere utilizzata, quindi ha limitato in qualche modo la sua usabilità. In realtà non la variabile completamente, ma la "classe" di configurazione che è essenzialmente il prefisso. Ma una volta definito il prefisso, è possibile utilizzare qualsiasi variabile senza modificarlapostgresql.conf


3
@BrijanElwadhi: sì, è transazionale.
a_horse_with_no_name

2
Come nota a margine: alcune parole sono riservate, ad esempio cambiando set session my.vars.id = '1';in set session my.user.id = '1';renderàERROR: syntax error at or near "user"
dominik

2
@BrijanElwadhi: Per rendere determinata operazione variabile è necessario utilizzare: SET LOCAL .... La sessionvariabile sarà in vigore fino a quando la connessione lo è. L' localambito è destinato alla transazione.
Eugen Konkov,

@dominik È possibile aggirare quella limitazione con virgolette, ad es. set session "my.user.id" = '1';La current_setting('my.user.id')chiamata funziona come previsto.
Miles Elam,

58

Dipende dal tuo cliente.

Tuttavia, se si utilizza il client psql , è possibile utilizzare quanto segue:

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

Se stai usando variabili di testo devi citare.

\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';

1
\setdeve essere minuscolo
deluan

db = # \ set profile_id 102 db = #: profile_id; ERRORE: errore di sintassi vicino o "102" LINEA 1: 102; ^
AlxVallejo

1
@AlxVallejo devi usarlo nelle istruzioni e nella console psql . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis,

21

Utilizzo di una tabella temporanea al di fuori di pl / PgSQL

A parte l'utilizzo di pl / pgsql o altro linguaggio pl / * come suggerito, questa è l'unica altra possibilità che mi viene in mente.

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

13

Voglio proporre un miglioramento alla risposta di @ DarioBarrionuevo , per semplificare l'utilizzo delle tabelle temporanee.

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;

bella soluzione per risolvere il blocco DO non può restituire il set di dati!
CodeFarmer

Su PostgreSQL 11.0, una query di questo tipo restituisce 1(presumibilmente il conteggio delle righe) anziché il contenuto di tmp_table.
Ed Noepel,

9

Questa soluzione si basa su quella proposta da fei0x ma presenta i vantaggi che non è necessario unire l'elenco dei valori delle costanti nella query e le costanti possono essere facilmente elencate all'inizio della query. Funziona anche con query ricorsive.

Fondamentalmente, ogni costante è una tabella a valore singolo dichiarata in una clausola WITH che può quindi essere chiamata ovunque nella parte rimanente della query.

  • Esempio di base con due costanti:
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)

In alternativa è possibile utilizzare al SELECT * FROM constant_nameposto del TABLE constant_namequale potrebbe non essere valido per altre lingue di query diverse da postgresql.


6

Ecco un esempio usando le istruzioni PREPARE . Non puoi ancora usare? , ma puoi usare la $nnotazione:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;

Funziona abbastanza bene! Grazie.
Rui Carvalho,

4

È vero, non esiste un modo vivido e inequivocabile per dichiarare una variabile a valore singolo, quello che puoi fare è

with myVar as (select "any value really")

quindi, per ottenere l'accesso al valore memorizzato in questa costruzione, lo fai

(select * from myVar)

per esempio

with var as (select 123)    
... where id = (select * from var)

3

È possibile ricorrere a strumenti con funzioni speciali. Come per la sintassi proprietaria di DBeaver:

@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);

Questo è più vicino all'utilizzabile: esaminerò se DBeaver supporta elenchi e cicli: ho bisogno di applicare lo stesso sql a più schemi e l'elenco sarebbe degli schemi a cui applicarli.
Javavba,

1

In DBeaver è possibile utilizzare i parametri nelle query proprio come è possibile dal codice, quindi funzionerà:

SELECT *
FROM somewhere
WHERE something = :myvar

Quando si esegue la query, DBeaver richiederà il valore per: myvar ed eseguirà la query.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.