Jak używać zmiennych skryptu w psql?

135

W MS SQL Server tworzę swoje skrypty do używania zmiennych konfigurowalnych:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

Następnie zmienię wartość @somevariablew czasie wykonywania, w zależności od wartości, której chcę w konkretnej sytuacji. Ponieważ znajduje się na górze skryptu, jest łatwy do zobaczenia i zapamiętania.

Jak mam zrobić to samo z klientem PostgreSQL psql?

Craig Walker
źródło
6
FWIW, operator \ set wydaje się być powiązany z narzędziem wiersza poleceń psql, a nie z językiem wsadowym pgsql. Mogę się mylić.
Daniel Yankowsky,
1
Z jakiej wersji Postgres korzystasz?
Kuberchaun

Odpowiedzi:

182

Zmienne Postgres są tworzone za pomocą polecenia \ set, na przykład ...

\set myvariable value

... i można go następnie zastąpić, na przykład, jako ...

SELECT * FROM :myvariable.table1;

... lub ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

edycja: Od wersji psql 9.1 zmienne można interpretować w cudzysłowach, jak w:

\set myvariable value 

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

W starszych wersjach klienta psql:

... Jeśli chcesz użyć zmiennej jako wartości w warunkowym zapytaniu łańcuchowym, takim jak ...

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

... wtedy musisz zawrzeć cudzysłowy w samej zmiennej, ponieważ powyższe nie zadziała. Zamiast tego zdefiniuj zmienną jako taką ...

\set myvariable 'value'

Jeśli jednak, tak jak ja, napotkasz sytuację, w której chciałeś utworzyć ciąg z istniejącej zmiennej, znalazłem trik w tym ...

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

Teraz masz zmienną w cudzysłowie i niecytowaną, składającą się z tego samego ciągu! I możesz zrobić coś takiego ....

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
crowmagnumb
źródło
69
\setjest tylko dla psqlnarzędzia, nie można go używać w procedurach składowanych!
sorin
6
@SorinSbarnea OP zapytał o scenariusz , a nie procedurę
Daniel Serodio
36
Ta odpowiedź w mylący sposób miesza psqlmeta-polecenia \setz poleceniami PostgreSQL.
Erwin Brandstetter
20
Począwszy od postgresql 9.1, w psql możesz teraz użyć: 'zmienna', aby poprawnie zacytować ją jako wartość lub: 'zmienna', aby użyć jej jako identyfikatora.
HitScan
9
\set myvariable 'value'czy nie zawierają żadnych cytatów wewnątrz zmiennej, w przeciwieństwie do tego, co mówi ta odpowiedź. W razie wątpliwości użyj \echo :myvariablew psql, aby wyświetlić wartość niezależnie od zapytania.
Daniel Vérité
62

Ostatnie słowo na temat zmiennych PSQL:

  1. Nie rozszerzają się, jeśli umieścisz je w pojedynczych cudzysłowach w instrukcji SQL. Zatem to nie działa:

    SELECT * FROM foo WHERE bar = ':myvariable'
    
  2. Aby rozwinąć do literału łańcuchowego w instrukcji SQL, musisz uwzględnić cudzysłowy w zestawie zmiennych. Jednak wartość zmiennej musi już być ujęta w cudzysłów, co oznacza, że ​​potrzebujesz drugiego zestawu cudzysłowów, a wewnętrzny zestaw musi zostać zmieniony. Potrzebujesz więc:

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

    EDYCJA : zaczynając od PostgreSQL 9.1, możesz zamiast tego napisać:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
    
Craig Walker
źródło
13
Pozdrawiam:'myvariable'
Andomar
Dokładnie to, czego szukałem!
KeniSteward
48

Możesz spróbować użyć klauzuli 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
źródło
Ten sposób jest najbardziej wygodny, gdy w zapytaniu używasz kilkukrotnie tych samych wartości obliczonych.
skaurus
2
W przeciwieństwie do raportu Bryce'a, wydaje mi się, że działa dobrze. 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; Wiek Ouputs Jack i Jack, zgodnie z oczekiwaniami.
Joshua,
2
W przypadku wielu zastosowań, szczególnie w kontekście struktury aplikacji internetowej, takiej jak Python Flask, jest to najlepsze rozwiązanie do ponownego wykorzystania złożonych obliczonych wartości w ramach jednego zapytania.
Will
1
Czy ktoś może zasugerować, jak to może działać we wkładce?
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,
35

W szczególności w przypadku psqlmożna również przekazywać psqlzmienne z wiersza poleceń; możesz je przekazać -v. Oto przykład użycia:

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

Zwróć uwagę, że dwukropek nie jest cytowany, a następnie cytowana jest nazwa zmiennej, do której należy ona. Wiem, dziwna składnia. Działa to tylko w psql; nie zadziała w (powiedzmy) PgAdmin-III.

To podstawienie ma miejsce podczas przetwarzania danych wejściowych w psql, więc nie możesz (powiedzmy) zdefiniować funkcji, która używa :'filepath'i oczekuje, że wartość :'filepath'zmieni się z sesji na sesję. Zostanie podstawiony raz, kiedy funkcja zostanie zdefiniowana, a potem będzie stała. Jest to przydatne do tworzenia skryptów, ale nie w czasie wykonywania.

Craig Ringer
źródło
zmienne psql, np .: 'filepath', wskazałeś: "Zauważ, że dwukropek nie jest cytowany, wtedy nazwa zmiennej jest cytowana w cudzysłowie." Podziękować! Ty! Włożyłem już na biurko kilka wgnieceń w kształcie czoła, próbując to zadziałać, a ty zaoszczędziłeś mi jeszcze tonę. Dokładnie to, czego potrzebowałem do niektórych skryptów.
Jason
14

FWIW, prawdziwy problem polegał na tym, że na końcu mojego polecenia \ set umieściłem średnik:

\ set owner_password 'thepassword';

Średnik został zinterpretowany jako rzeczywisty znak w zmiennej:

\ echo: hasło_właściciela hasło;

Więc kiedy próbowałem go użyć:

UTWÓRZ ROLĘ myrole ZALOGUJ NIEZASZYFROWANE HASŁO: hasło_właściciela NOINHERIT CREATEDB CREATEROLE WAŻNE DO 'nieskończoności';

...Mam to:

UTWÓRZ ROLĘ myrole ZALOGUJ NIEZASZYFROWANE HASŁO thepassword; NOINHERIT CREATEDB CREATEROLE WAŻNE DO „nieskończoności”;

To nie tylko nie spowodowało ustawienia cudzysłowów wokół literału, ale podzieliło polecenie na 2 części (z których druga była nieprawidłowa, ponieważ zaczynała się od „NOINHERIT”).

Morał z tej historii: „zmienne” PostgreSQL to tak naprawdę makra używane do interpretacji tekstu, a nie prawdziwe wartości. Jestem pewien, że to się przydaje, ale na początku jest trudne.

Craig Walker
źródło
12

Musisz użyć jednego z języków proceduralnych, takich jak PL / pgSQL, a nie języka SQL proc. W PL / pgSQL możesz używać zmiennych bezpośrednio w instrukcjach SQL. W przypadku pojedynczych cudzysłowów możesz użyć funkcji literału cudzysłowu.


źródło
5
Nie można tego zrobić w samym Postgresie, ale można to zrobić w aplikacji klienckiej PSQL.
Philluminati
1
plpgsql może (teraz) być używany w postgres (od wersji 9.0)) postgresql.org/docs/9.0/static/sql-do.html
Jasen
11

postgres (od wersji 9.0) pozwala na anonimowe bloki w każdym z obsługiwanych języków skryptów po stronie serwera

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

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

Ponieważ wszystko znajduje się wewnątrz łańcucha, zewnętrzne zmienne łańcuchowe, które są podstawiane, będą musiały zostać umieszczone dwukrotnie w cudzysłowie. Użycie zamiast tego cudzysłowu nie zapewni pełnej ochrony przed wstrzyknięciem SQL.

Jasen
źródło
5

Innym podejściem jest (ab) użycie mechanizmu PostgreSQL GUC do tworzenia zmiennych. Zobacz tę wcześniejszą odpowiedź, aby uzyskać szczegółowe informacje i przykłady.

Deklarujesz GUC w postgresql.conf, a następnie zmieniasz jego wartość w czasie wykonywania za pomocą SETpoleceń i uzyskujesz jego wartość za pomocą current_setting(...).

Nie polecam tego do ogólnego użytku, ale może być przydatne w wąskich przypadkach, takich jak ten wspomniany w powiązanym pytaniu, w którym plakat chciał zapewnić sposób podania nazwy użytkownika na poziomie aplikacji do wyzwalaczy i funkcji.

Craig Ringer
źródło
4

Rozwiązałem to za pomocą tabeli tymczasowej.

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

W ten sposób miałem „zmienną”, której mogłem użyć w wielu zapytaniach, która jest unikalna dla sesji. Potrzebowałem go do generowania unikalnych „nazw użytkowników”, jednocześnie nie mając kolizji podczas importowania użytkowników o tej samej nazwie użytkownika.

geon
źródło
Wydaje się, że jest to jedyny działający sposób w narzędziach wizualnych, takich jak Heidi SQL.
Altair7852
2

Uważam, że to pytanie i odpowiedzi są niezwykle przydatne, ale także zagmatwane. Miałem wiele problemów z uruchomieniem cytowanych zmiennych, więc oto sposób, w jaki je uruchomiłem:

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

W ten sposób możesz zdefiniować zmienną w jednej instrukcji. Kiedy go użyjesz, pojedyncze cudzysłowy zostaną osadzone w zmiennej.

UWAGA! Kiedy umieściłem komentarz po cytowanej zmiennej, został on zassany jako część zmiennej, gdy wypróbowałem niektóre metody w innych odpowiedziach. To naprawdę mnie wkurzało przez jakiś czas. Dzięki tej metodzie komentarze wydają się być traktowane zgodnie z oczekiwaniami.

Nate
źródło
\set deployment_pass 'string_password' ALTER USER :deployment_user WITH PASSWORD :'deployment_pass';
Jasen
\ set nie jest SQL to wbudowane polecenie psql. komentarze sql nie są obsługiwane.
Jasen
2

Naprawdę tęsknię za tą funkcją. Jedynym sposobem osiągnięcia czegoś podobnego jest użycie funkcji.

Użyłem go na dwa sposoby:

  • funkcje Perla, które używają zmiennej $ _SHARED
  • przechowuj zmienne w tabeli

Wersja Perla:

   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;

Wersja stołowa:

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

Uwagi:

  • plperlu jest szybszy niż perl
  • pg_backend_pid nie jest najlepszą identyfikacją sesji, rozważ użycie pid w połączeniu z backend_start z pg_stat_activity
  • ta wersja tabeli jest również zła, ponieważ musisz ją wyczyścić od czasu do czasu (i nie usuwać aktualnie pracujących zmiennych sesji)
Kaiko Kaur
źródło
1

Zmienne w psqlssaniu. Jeśli chcesz zadeklarować liczbę całkowitą, musisz wprowadzić tę liczbę, następnie wykonać znak powrotu karetki, a następnie zakończyć instrukcję średnikiem. Przestrzegać:

Powiedzmy, że chcę zadeklarować zmienną całkowitą my_vari wstawić ją do tabeli test:

Przykładowa tabela 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)

Oczywiście w tej tabeli nie ma jeszcze nic:

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

Deklarujemy zmienną. Zwróć uwagę, że średnik znajduje się w następnej linii!

thedatabase=# \set my_var 999
thedatabase=# ;

Teraz możemy wstawić. Musimy użyć tej dziwnie :''wyglądającej " " składni:

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

Zadziałało!

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

Wyjaśnienie:

Więc ... co się stanie, jeśli w następnym wierszu nie będzie średnika? Zmienna? Spójrz:

Deklarujemy my_varbez nowej linii.

thedatabase=# \set my_var 999;

Wybierzmy my_var.

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

Co to do cholery jest? To nie jest liczba całkowita , to ciąg znaków 999; !

thedatabase=# select 999;
 ?column? 
----------
      999
(1 row)
Alexander Kleinhans
źródło
5
Powodem, dla którego średnik powoduje nieoczekiwane rzeczy, jest to, że średnik kończy instrukcję SQL, ale wpisujesz polecenie psql, \ set, które nie jest językiem SQL i NIE przyjmuje kończącego średnika. Umieszczenie średnika w następnej linii nie zaszkodzi, ale absolutnie nic nie zrobi. To puste stwierdzenie.
volkerk
1

Opublikowałem nowe rozwiązanie tego problemu w innym wątku .

Używa tabeli do przechowywania zmiennych i może być aktualizowana w dowolnym momencie. Statyczna niezmienna funkcja pobierająca jest tworzona dynamicznie (przez inną funkcję), wyzwalana przez aktualizację tabeli. Otrzymujesz niezłe miejsce do przechowywania, a także niesamowitą prędkość niezmiennego gettera.

Brev
źródło