Come usi le variabili di script in psql?


133

In MS SQL Server, creo i miei script per utilizzare variabili personalizzabili:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

Quindi cambierò il valore di @somevariable runtime, a seconda del valore che voglio nella situazione particolare. Dal momento che è nella parte superiore della sceneggiatura è facile da vedere e ricordare.

Come posso fare lo stesso con il client PostgreSQL psql?


5
FWIW, l'operatore \ set sembra essere correlato allo strumento da riga di comando psql, non al linguaggio batch pgsql. Potrei sbagliarmi.
Daniel Yankowsky,

1
Su quale versione di Postgres sei?
Kuberchaun,

Risposte:


180

Le variabili Postgres vengono create tramite il comando \ set, ad esempio ...

\set myvariable value

... e può quindi essere sostituito, ad esempio, come ...

SELECT * FROM :myvariable.table1;

... o ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

modifica: A partire da psql 9.1, le variabili possono essere espanse tra virgolette come in:

\set myvariable value 

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

Nelle versioni precedenti del client psql:

... Se si desidera utilizzare la variabile come valore in una query con stringa condizionale, come ...

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

... quindi è necessario includere le virgolette nella variabile stessa poiché quanto sopra non funzionerà. Definisci invece la tua variabile come tale ...

\set myvariable 'value'

Tuttavia, se, come me, ti sei imbattuto in una situazione in cui volevi creare una stringa da una variabile esistente, ho trovato il trucco per essere questo ...

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

Ora hai una variabile quotata e non quotata della stessa stringa! E puoi fare qualcosa del genere ....

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

66
\setè solo per lo psqlstrumento, non è possibile utilizzarlo nelle procedure memorizzate!
sorin,

6
@SorinSbarnea l'OP ha chiesto della sceneggiatura , non della procedura
Daniel Serodio,

35
Questa risposta mescola i psqlmeta-comandi \setcon i comandi PostgreSQL in modo confuso.
Erwin Brandstetter,

20
A partire da Postgresql 9.1, in psql ora puoi usare: 'variabile' per averlo correttamente citato come valore per te, oppure: "variabile" per usarlo come identificatore.
HitScan,

9
\set myvariable 'value'non non includere qualsiasi citazione all'interno della variabile, al contrario di ciò che dice questa risposta. In caso di dubbi, utilizzare \echo :myvariableall'interno di psql per visualizzare il valore indipendentemente da qualsiasi query.
Daniel Vérité,

61

Un'ultima parola sulle variabili PSQL:

  1. Non si espandono se li racchiudi tra virgolette singole nell'istruzione SQL. Quindi questo non funziona:

    SELECT * FROM foo WHERE bar = ':myvariable'
  2. Per espandersi in una stringa letterale in un'istruzione SQL, è necessario includere le virgolette nel set di variabili. Tuttavia, il valore della variabile deve già essere racchiuso tra virgolette, il che significa che è necessario un secondo set di virgolette e il set interno deve essere sottoposto a escape. Quindi hai bisogno di:

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

    EDIT : a partire da PostgreSQL 9.1, puoi scrivere invece:

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

12
Saluti per:'myvariable'
Andomar,

Esattamente quello che stavo cercando!
KeniSteward,

47

Puoi provare a usare una clausola 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;

In questo modo è utile soprattutto quando si utilizzano più volte gli stessi valori calcolati nella query.
skaurus

2
Contrariamente alla relazione di Bryce, sembra funzionare bene per me. 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; Sovrappone l'età di Jack e Jack, come previsto.
Giosuè,

2
Per molti usi, specialmente nel contesto di un framework di applicazioni web come Python Flask, questa è la soluzione migliore per riutilizzare valori calcolati complessi all'interno di una singola query.
Sarà il

1
Qualcuno può suggerire come potrebbe funzionare in un inserto?
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

In particolare psql, è possibile passare psqlvariabili anche dalla riga di comando; puoi passarli con -v. Ecco un esempio di utilizzo:

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

Si noti che i due punti non sono quotati, quindi viene citato il nome della variabile stesso. Sintassi strana, lo so. Funziona solo in psql; non funzionerà in (diciamo) PgAdmin-III.

Questa sostituzione avviene durante l'elaborazione dell'input in psql, quindi non è possibile (dire) definire una funzione che utilizza :'filepath'e si aspetta che il valore :'filepath'cambi da una sessione all'altra. Verrà sostituito una volta, quando viene definita la funzione, e successivamente sarà una costante. È utile per gli script ma non per l'uso in runtime.


variabili psql, ad esempio: 'percorso file', hai sottolineato: "Nota che i due punti non sono quotati, quindi viene citato il nome della variabile stesso". Grazie! Tu! Ho già messo un mucchio di ammaccature a forma di fronte nella mia scrivania, cercando di farlo funzionare, e tu mi hai salvato un sacco di più. Esattamente quello che mi serviva per alcuni script.
Jason,

13

FWIW, il vero problema era che avevo incluso un punto e virgola alla fine del mio comando \ set:

\ set owner_password 'thepassword';

Il punto e virgola è stato interpretato come un carattere reale nella variabile:

\ echo: owner_password thepassword;

Quindi quando ho provato ad usarlo:

CREATE ROLE myrole ACCEDI PASSWORD NON CRIPTATO: owner_password NOINHERIT CREATEDB CREATEROLE VALID FINO A 'infinito';

...Ho capito:

CREATE ROLE myrole LOGIN PASSWORD NON CRIPTATO thepassword; NOINHERIT CREATEDB CREATEROLE VALID FINO A 'infinito';

Ciò non solo non è riuscito a impostare le virgolette attorno al valore letterale, ma ha diviso il comando in 2 parti (la seconda delle quali non era valida poiché era iniziata con "NOINHERIT").

La morale di questa storia: le "variabili" PostgreSQL sono in realtà macro utilizzate nell'espansione del testo, non valori reali. Sono sicuro che tornerà utile, ma all'inizio è difficile.


12

È necessario utilizzare uno dei linguaggi procedurali come PL / pgSQL e non il linguaggio proc SQL. In PL / pgSQL puoi usare vars proprio nelle istruzioni SQL. Per le virgolette singole è possibile utilizzare la funzione letterale delle virgolette.


5
Non può essere eseguito in Postgres stesso, ma può essere eseguito nell'applicazione client PSQL.
Philluminati

1
plpgsql può (ora) essere utilizzato in postgres (dalla versione 9.0)) postgresql.org/docs/9.0/static/sql-do.html
Jasen,

11

Postgres (dalla versione 9.0) consente blocchi anonimi in uno dei linguaggi di scripting lato server supportati

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

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

Poiché tutto è all'interno di una stringa, le variabili stringa esterne sostituite dovranno essere salvate e citate due volte. L'uso della quotazione in dollari invece non fornirà una protezione completa contro l'iniezione SQL.


5

Un altro approccio è (ab) utilizzare il meccanismo GUC PostgreSQL per creare variabili. Vedi questa risposta precedente per dettagli ed esempi.

Si dichiara GUC in postgresql.conf, quindi si modifica il suo valore in fase di esecuzione con i SETcomandi e si ottiene il suo valore concurrent_setting(...) .

Non lo consiglio per un uso generale, ma potrebbe essere utile in casi ristretti come quello menzionato nella domanda collegata, in cui il poster voleva un modo per fornire il nome utente a livello di applicazione per trigger e funzioni.


4

L'ho risolto con un tavolo temporaneo.

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

In questo modo, avevo una "variabile" che potevo usare su più query, unica per la sessione. Ne avevo bisogno per generare "nomi utente" univoci pur non avendo collisioni se si importano utenti con lo stesso nome utente.


Questo sembra essere l'unico modo di lavorare in strumenti visivi come Heidi SQL.
Altair7852,

2

Ho trovato questa domanda e le risposte estremamente utili, ma anche confuse. Ho avuto molti problemi a far funzionare le variabili tra virgolette, quindi ecco come l'ho fatto funzionare:

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

In questo modo è possibile definire la variabile in un'istruzione. Quando lo usi, le virgolette singole verranno incorporate nella variabile.

NOTA! Quando ho inserito un commento dopo la variabile citata, questo è stato risucchiato come parte della variabile quando ho provato alcuni dei metodi in altre risposte. Questo mi ha davvero rovinato per un po '. Con questo metodo i commenti sembrano essere trattati come ti aspetteresti.


\set deployment_pass 'string_password' ALTER USER :deployment_user WITH PASSWORD :'deployment_pass';
Jasen,

\ set non è SQL, è un comando psql incorporato i commenti sql non sono supportati.
Jasen,

2

Mi manca molto quella caratteristica. L'unico modo per ottenere qualcosa di simile è usare le funzioni.

L'ho usato in due modi:

  • funzioni perl che utilizzano la variabile $ _SHARED
  • archivia le variabili nella tabella

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

Versione da tavolo:

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

Appunti:

  • plperlu è più veloce del perl
  • pg_backend_pid non è la migliore identificazione della sessione, considera l'utilizzo di pid combinato con backend_start da pg_stat_activity
  • anche questa versione della tabella è errata perché è necessario cancellarla occasionalmente (e non eliminare le variabili di sessione attualmente funzionanti)

1

Variabili in psql succhiare. Se si desidera dichiarare un numero intero, è necessario immettere il numero intero, quindi eseguire un ritorno a capo, quindi terminare l'istruzione in punto e virgola. Osservare:

Diciamo che voglio dichiarare una variabile intera my_vare inserirla in una tabella test:

Tabella di esempio 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)

Chiaramente, nulla in questa tabella ancora:

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

Dichiariamo una variabile. Nota come è il punto e virgola nella riga successiva!

thedatabase=# \set my_var 999
thedatabase=# ;

Ora possiamo inserire. Dobbiamo usare questa " :''" sintassi dall'aspetto strano :

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

Ha funzionato!

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

Spiegazione:

Quindi ... cosa succede se non abbiamo il punto e virgola nella riga successiva? La variabile? Dare un'occhiata:

Dichiariamo my_varsenza la nuova linea.

thedatabase=# \set my_var 999;

Selezioniamo my_var.

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

Che cos'è questo? Non è un numero intero , è una stringa 999; !

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

5
Il motivo per cui il punto e virgola fa cose inaspettate per te è che un punto e virgola termina un'istruzione SQL, ma stai digitando un comando psql, \ set, che non è SQL e NON accetta un punto e virgola terminante. Mettere un punto e virgola nella riga successiva non farà male, ma non fa assolutamente nulla. È un'affermazione vuota.
Volkerk,

1

Ho pubblicato una nuova soluzione per questo su un altro thread .

Utilizza una tabella per memorizzare le variabili e può essere aggiornato in qualsiasi momento. Una funzione getter statica immutabile viene creata dinamicamente (da un'altra funzione), attivata dall'aggiornamento della tabella. Ottieni una buona conservazione dei tavoli, oltre alle straordinarie velocità di un immutabile getter.

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.