Jak zadeklarować zmienną w zapytaniu PostgreSQL

241

Jak zadeklarować zmienną do użycia w zapytaniu PostgreSQL 8.3?

W MS SQL Server mogę to zrobić:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Jak zrobić to samo w PostgreSQL? Zgodnie z dokumentacją zmienne są zadeklarowane po prostu jako „typ nazwy”, ale daje mi to błąd składniowy:

myvar INTEGER;

Czy ktoś mógłby mi podać przykład poprawnej składni?

EMP
źródło
2
Można to zrobić tylko w PostgreSQL. Zobacz odpowiedź na to powiązane pytanie: stackoverflow.com/questions/766657/…
Sean the Bean
2
Ta pokrewna odpowiedź ma lepsze odpowiedzi: stackoverflow.com/questions/13316773/…
Erwin Brandstetter

Odpowiedzi:

113

W PostgreSQL nie ma takiej funkcji. Możesz to zrobić tylko w pl / PgSQL (lub innym pl / *), ale nie w zwykłym SQL.

Wyjątkiem jest WITH ()zapytanie, które może działać jako zmienna, a nawet tuplezmienne. Pozwala zwrócić tabelę wartości tymczasowych.

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.Wincewicz
źródło
Wypróbowałem tę metodę CTE jako vriables. Ale potem szybko natknąłem się na problem polegający na tym, że różne zapytania modyfikujące dane w CTE nie gwarantują wzajemnego efektu. Musiałem użyć wielu CTE, ponieważ potrzebowałem tej zmiennej w wielu zapytaniach.
Zia Ul Rehman Mughal
228

Osiągnąłem ten sam cel, używając WITHklauzuli , nie jest to tak eleganckie, ale może zrobić to samo. Chociaż w tym przykładzie jest to naprawdę przesada. Też nie polecam tego szczególnie.

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;
fei0x
źródło
2
Działa to świetnie w większości przypadków, w których chciałbyś mieć zmienne. Jeśli jednak chcesz użyć zmiennej dla LIMIT (która nie może zawierać zmiennych), powinieneś użyć jej \setzgodnie z sugestią Shahriara Aghajaniego.
cimmanon
1
Jest to idealne rozwiązanie, gdy mam skrypt migracji, w którym chcę zaimportować niektóre dane relacyjne. Oczywiście nie będę znać identyfikatora sekwencji podanego przez dane relacyjne.
Relequestual
3
Właśnie wypróbowałem to podejście i znalazłem być może lepszy sposób: JOIN myconstants ON truea potem nie ma potrzeby wykonywania wyboru dodatkowego.
vektor
7
Działa to tylko w ramach jednego zapytania, nie można udostępniać WITHCTE między zapytaniami w transakcji.
Daenyth,
2
Stare pytanie, ale tutaj oto odmiana: WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. CROSS JOIN, będąc wyrażeniem tabeli z jednym wierszem, wirtualnie powiela dane dla wszystkich wierszy w prawdziwej tabeli i upraszcza wyrażenie.
Manngo,
82

Możesz także spróbować tego w 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;

Powyższe wymaga Postgres 9.0 lub nowszego.

Dario Barrionuevo
źródło
1
Instrukcja DO została dodana w PostgreSQL 9.0 i nie działa w 8.3.
Johny,
14
Użyj opcji UTWÓRZ TABELĘ TYMCZASOWĄ lub UTWÓRZ TABELĘ TEMPORACYJNĄ, a nie UTWÓRZ TABELĘ. Ale poza tym w porządku.
Stefan Steiger
60

Ustawienia dynamicznej konfiguracji

możesz „nadużyć” ustawień dynamicznej konfiguracji w tym celu:

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

Ustawienia konfiguracji są zawsze wartościami varchar, więc podczas ich używania musisz rzutować je na poprawny typ danych. Działa to z każdym klientem SQL, podczas gdy \setdziała tylko wpsql

Powyższe wymaga Postgres 9.2 lub nowszego.

W poprzednich wersjach zmienną trzeba było zadeklarować w postgresql.conf przed użyciem, więc ograniczyła nieco jej użyteczność. W rzeczywistości nie jest to zmienna całkowicie, ale konfiguracyjna „klasa”, która jest zasadniczo przedrostkiem. Ale po zdefiniowaniu prefiksu można użyć dowolnej zmiennej bez zmianypostgresql.conf

koń bez imienia
źródło
3
@BrijanElwadhi: tak, to jest transakcyjne.
a_horse_w_no_name
2
Na marginesie: niektóre słowa są zastrzeżone, na przykład zmiana set session my.vars.id = '1';na set session my.user.id = '1';przyniesieERROR: syntax error at or near "user"
dominik
2
@BrijanElwadhi: Aby zmiennej określonej transakcji należy użyć: SET LOCAL .... sessionZmienna będzie obowiązywać tak długo, jak połączenie jest. Zakres localobejmuje transakcje.
Eugen Konkov,
@dominik Można obejść to ograniczenie z cytatami, np., wezwanie działa zgodnie z oczekiwaniami. set session "my.user.id" = '1';current_setting('my.user.id')
Miles Elam
58

To zależy od twojego klienta.

Jeśli jednak używasz klienta psql , możesz użyć następujących opcji:

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

Jeśli używasz zmiennych tekstowych, musisz zacytować.

\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';
Shahriar Aghajani
źródło
1
\setmuszą być
pisane
db = # \ set ID_profilu 102 db = #: ID_profilu; BŁĄD: błąd składniowy w linii „102” lub w jej pobliżu LINE 1: 102; ^
AlxVallejo
1
@AlxVallejo musisz użyć go w instrukcji i konsoli psql . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis
21

Używanie tabeli temperatur poza pl / PgSQL

Poza użyciem sugerowanego języka pl / pgsql lub innego języka pl / *, jest to jedyna inna możliwość, o której mogłem pomyśleć.

begin;
select 5::int as var into temp table myvar;
select *
  from somewhere s, myvar v
 where s.something = v.var;
commit;
Evan Carroll
źródło
13

Chcę zaproponować ulepszenie odpowiedzi @ DarioBarrionuevo , aby ułatwić korzystanie z tabel tymczasowych.

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;
niebieskawy
źródło
fajne rozwiązanie do rozwiązywania blok DO nie może zwrócić zestawu danych!
CodeFarmer
Na PostgreSQL 11.0 takie zapytanie zwraca 1(prawdopodobnie liczbę wierszy), a nie zawartość tmp_table.
Ed Noepel
9

To rozwiązanie jest oparte na rozwiązaniu zaproponowanym przez fei0x, ale ma tę zaletę, że nie ma potrzeby dołączania do listy wartości stałych w zapytaniu, a stałe można łatwo wymienić na początku zapytania. Działa również w zapytaniach rekurencyjnych.

Zasadniczo każda stała jest tabelą pojedynczej wartości zadeklarowaną w klauzuli WITH, którą można następnie wywołać w dowolnym miejscu w pozostałej części zapytania.

  • Podstawowy przykład z dwiema stałymi:
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)

Alternatywnie możesz użyć SELECT * FROM constant_namezamiast tego, TABLE constant_namektóry może być niepoprawny dla innych języków zapytań innych niż postgresql.

Jorge Luis
źródło
6

Oto przykład z użyciem instrukcji PREPARE . Nadal nie możesz używać ?, ale możesz używać $nnotacji:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;
Martin Zinovsky
źródło
Działa całkiem dobrze! Dzięki.
Rui Carvalho
4

To prawda, że ​​nie ma wyraźnego i jednoznacznego sposobu zadeklarowania zmiennej o jednej wartości, co możesz zrobić

with myVar as (select "any value really")

następnie, aby uzyskać dostęp do wartości przechowywanej w tej konstrukcji, robisz to

(select * from myVar)

na przykład

with var as (select 123)    
... where id = (select * from var)
Michael.Medvedskiy
źródło
3

Możesz skorzystać z funkcji specjalnych narzędzi. Podobnie jak w przypadku własnej własności składni 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);
gavenkoa
źródło
Jest to bliższe użytecznemu: zamierzam sprawdzić, czy DBeaver obsługuje listy i zapętlanie: muszę zastosować ten sam kod SQL do wielu schematów, a lista zawiera schematy, w których można je zastosować.
javadba
1

W DBeaver możesz używać parametrów w zapytaniach, tak jak możesz to zrobić z kodu, więc to zadziała:

SELECT *
FROM somewhere
WHERE something = :myvar

Po uruchomieniu zapytania DBeaver zapyta o wartość: myvar i uruchom zapytanie.

The Coder
źródło