Cara mendeklarasikan variabel dalam kueri PostgreSQL

242

Bagaimana cara mendeklarasikan variabel untuk digunakan dalam kueri PostgreSQL 8.3?

Di MS SQL Server saya bisa melakukan ini:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Bagaimana saya melakukan hal yang sama di PostgreSQL? Menurut variabel dokumentasi dinyatakan hanya sebagai "tipe nama;", tetapi ini memberi saya kesalahan sintaks:

myvar INTEGER;

Bisakah seseorang memberi saya contoh sintaks yang benar?

EMP
sumber
2
Itu bisa dilakukan hanya di PostgreSQL. Lihat jawaban untuk pertanyaan terkait ini: stackoverflow.com/questions/766657/…
Sean the Bean
2
Jawaban terkait ini memiliki jawaban yang lebih baik: stackoverflow.com/questions/13316773/…
Erwin Brandstetter

Jawaban:

113

Tidak ada fitur seperti itu di PostgreSQL. Anda dapat melakukannya hanya di pl / PgSQL (atau pl / * lainnya), tetapi tidak dalam SQL biasa.

Pengecualian adalah WITH ()kueri yang bisa berfungsi sebagai variabel, atau bahkan tuplevariabel. Ini memungkinkan Anda untuk mengembalikan tabel nilai sementara.

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
sumber
Saya mencoba metode CTE yang digunakan sebagai vriables. Tapi daripada saya dengan cepat mengalami masalah di mana berbagai permintaan memodifikasi data dalam CTE tidak dijamin untuk melihat efek masing-masing. Saya harus menggunakan beberapa CTE karena saya perlu menggunakan variabel itu di beberapa kueri.
Zia Ul Rehman Mughal
228

Saya mencapai tujuan yang sama dengan menggunakan WITHklausa , itu sama sekali tidak elegan tetapi dapat melakukan hal yang sama. Meskipun untuk contoh ini benar-benar berlebihan. Saya juga tidak terlalu merekomendasikan ini.

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;
fei0x
sumber
2
Ini berfungsi baik untuk sebagian besar contoh di mana Anda ingin variabel. Namun, jika Anda ingin menggunakan variabel untuk LIMIT (yang tidak dapat berisi variabel), maka Anda ingin menggunakan \setseperti yang disarankan dalam jawaban Shahriar Aghajani.
cimmanon
1
Ini sangat ideal untuk ketika saya memiliki skrip migrasi tempat saya ingin mengimpor beberapa data relasional. Jelas saya tidak akan tahu id urutan data relasional diberikan.
Relequestual
3
Saya baru saja mencoba pendekatan ini, dan menemukan cara yang mungkin lebih baik: JOIN myconstants ON truedan kemudian tidak perlu melakukan sub-pilih.
vektor
7
Ini hanya berfungsi dalam satu permintaan, Anda tidak dapat membagikan WITHCTE lintas permintaan dalam transaksi.
Daenyth
2
Pertanyaan lama, tapi di sini inilah variasi: WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. CROSS JOIN, menjadi dengan ekspresi tabel baris tunggal, secara virtual menduplikasi data untuk semua baris dalam tabel nyata, dan menyederhanakan ekspresi.
Manngo
83

Anda juga dapat mencoba ini di 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;

Di atas membutuhkan Postgres 9.0 atau lebih tinggi.

Dario Barrionuevo
sumber
1
Pernyataan DO ditambahkan dalam PostgreSQL 9.0 dan tidak berfungsi di 8.3.
Johny
14
Gunakan CREATE TABLE TEMPORARY atau CREATE TABLETABLE, bukan CREATE TABLE. Tetapi sebaliknya baik-baik saja.
Stefan Steiger
60

Pengaturan Konfigurasi Dinamis

Anda dapat "menyalahgunakan" pengaturan konfigurasi dinamis untuk ini:

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

Pengaturan konfigurasi selalu merupakan nilai varchar, jadi Anda harus mengubahnya ke tipe data yang benar saat menggunakannya. Ini berfungsi dengan klien SQL mana pun sedangkan \sethanya bekerja dipsql

Di atas membutuhkan Postgres 9.2 atau yang lebih baru.

Untuk versi sebelumnya, variabel harus dideklarasikan postgresql.confsebelum digunakan, sehingga agak membatasi kegunaannya. Sebenarnya bukan variabel sepenuhnya, tetapi konfigurasi "kelas" yang pada dasarnya adalah awalan. Tetapi begitu awalan didefinisikan, variabel apa pun dapat digunakan tanpa mengubahpostgresql.conf

seekor kuda tanpa nama
sumber
3
@ BrijanElwadhi: ya itu transaksional.
a_horse_with_no_name
2
Sebagai catatan tambahan: beberapa kata dicadangkan, misalnya mengubah set session my.vars.id = '1';ke set session my.user.id = '1';akan menghasilkanERROR: syntax error at or near "user"
dominik
2
@BrijanElwadhi: Untuk membuat spesifik transaksi variabel Anda harus menggunakan: SET LOCAL .... The sessionvariabel akan berlaku selama Anda koneksi. The localdijangkau untuk transaksi.
Eugen Konkov
@dominik Anda bisa menyiasati keterbatasan itu dengan tanda kutip, misalnya., set session "my.user.id" = '1';The current_setting('my.user.id')panggilan bekerja seperti yang diharapkan.
Miles Elam
58

Itu tergantung pada klien Anda.

Namun, jika Anda menggunakan klien psql , maka Anda dapat menggunakan yang berikut ini:

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

Jika Anda menggunakan variabel teks, Anda perlu mengutip.

\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';
Shahriar Aghajani
sumber
1
\setharus huruf kecil
deluan
db = # \ set profile_id 102 db = #: profile_id; GALAT: kesalahan sintaks pada atau dekat "102" LINE 1: 102; ^
AlxVallejo
1
@AlxVallejo Anda harus menggunakannya dalam pernyataan dan konsol psql . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis
21

Menggunakan Tabel Temp di luar pl / PgSQL

Di luar menggunakan pl / pgsql atau bahasa pl / * lainnya seperti yang disarankan, ini adalah satu-satunya kemungkinan lain yang dapat saya pikirkan.

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

Saya ingin mengusulkan perbaikan untuk jawaban DarioBarrionuevo @ , untuk membuatnya lebih sederhana memanfaatkan tabel sementara.

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;
kebiruan
sumber
solusi bagus untuk memecahkan blok DO tidak dapat mengembalikan set data!
CodeFarmer
Pada PostgreSQL 11.0, permintaan seperti itu kembali 1(mungkin jumlah baris) daripada isi tmp_table.
Ed Noepel
9

Solusi ini didasarkan pada yang diusulkan oleh fei0x tetapi memiliki keuntungan bahwa tidak perlu bergabung dengan daftar nilai konstanta dalam kueri dan konstanta dapat dengan mudah dicantumkan pada awal kueri. Ini juga berfungsi dalam permintaan rekursif.

Pada dasarnya, setiap konstanta adalah tabel nilai tunggal yang dideklarasikan dalam klausa DENGAN yang kemudian dapat dipanggil di mana saja di bagian kueri yang tersisa.

  • Contoh dasar dengan dua konstanta:
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)

Atau Anda dapat menggunakan SELECT * FROM constant_namebukannya TABLE constant_nameyang mungkin tidak berlaku untuk bahasa query lain yang berbeda untuk postgresql.

Jorge Luis
sumber
6

Berikut adalah contoh menggunakan pernyataan SIAPKAN . Anda masih tidak dapat menggunakan ?, tetapi Anda dapat menggunakan $nnotasi:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;
Martin Zinovsky
sumber
Bekerja dengan sangat baik! Terima kasih.
Rui Carvalho
4

Benar, tidak ada cara yang jelas dan tidak ambigu untuk mendeklarasikan variabel nilai tunggal, yang dapat Anda lakukan adalah

with myVar as (select "any value really")

kemudian, untuk mendapatkan akses ke nilai yang disimpan dalam konstruksi ini, Anda lakukan

(select * from myVar)

sebagai contoh

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

Anda dapat menggunakan fitur khusus alat. Seperti untuk sintaks kepemilikan DBeaver sendiri:

@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
sumber
Ini lebih dekat ke yang bisa digunakan: saya akan melihat apakah DBeaver mendukung daftar dan mengulang: saya perlu menerapkan sql yang sama ke beberapa skema dan daftar akan menjadi skema untuk menerapkannya.
javadba
1

Di DBeaver Anda bisa menggunakan parameter dalam kueri seperti yang Anda dapat dari kode, jadi ini akan berfungsi:

SELECT *
FROM somewhere
WHERE something = :myvar

Ketika Anda menjalankan kueri, DBeaver akan meminta nilai untuk: myvar dan menjalankan kueri.

Sang Coder
sumber