Bagaimana Anda menggunakan variabel skrip di psql?

133

Di MS SQL Server, saya membuat skrip untuk menggunakan variabel yang dapat disesuaikan:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

Saya kemudian akan mengubah nilai @somevariablesaat runtime, tergantung pada nilai yang saya inginkan dalam situasi tertentu. Karena berada di bagian atas skrip, mudah dilihat dan diingat.

Bagaimana saya melakukan hal yang sama dengan klien PostgreSQL psql?

Craig Walker
sumber
5
FWIW, operator \ set tampaknya terkait dengan alat baris perintah psql, bukan ke bahasa batch pgsql. Saya bisa saja salah.
Daniel Yankowsky
1
Anda berada di versi Postgres apa?
Kuberchaun

Jawaban:

180

Variabel Postgres dibuat melalui perintah \ set, misalnya ...

\set myvariable value

... dan kemudian dapat diganti, misalnya, seperti ...

SELECT * FROM :myvariable.table1;

... atau ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

sunting: Pada psql 9.1, variabel dapat diperluas dalam tanda kutip seperti pada:

\set myvariable value 

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

Dalam versi yang lebih lama dari klien psql:

... Jika Anda ingin menggunakan variabel sebagai nilai dalam kueri string bersyarat, seperti ...

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

... maka Anda perlu memasukkan tanda kutip dalam variabel itu sendiri karena hal di atas tidak akan berfungsi. Alih-alih, tentukan variabel Anda sebagai ...

\set myvariable 'value'

Namun, jika, seperti saya, Anda mengalami situasi di mana Anda ingin membuat string dari variabel yang ada, saya menemukan trik untuk menjadi ini ...

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

Sekarang Anda memiliki variabel yang dikutip dan tidak dikutip dari string yang sama! Dan Anda dapat melakukan sesuatu seperti ini ....

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
crowmagnumb
sumber
66
\sethanya untuk psqlalat, Anda tidak dapat menggunakannya dalam prosedur tersimpan!
sorin
6
@SorinSbarnea OP bertanya tentang skrip , bukan prosedur
Daniel Serodio
35
Jawaban ini mencampur psqlmeta-perintah \setdengan perintah PostgreSQL dengan cara yang membingungkan.
Erwin Brandstetter
20
Pada postgresql 9.1, dalam psql Anda sekarang dapat menggunakan: 'variabel' untuk membuatnya dikutip dengan benar sebagai nilai untuk Anda, atau: "variabel" untuk menggunakannya sebagai pengidentifikasi.
HitScan
9
\set myvariable 'value'tidak tidak termasuk kutipan apapun di dalam variabel, bertentangan dengan apa yang dikatakan jawaban ini. Jika ragu, gunakan \echo :myvariabledalam psql untuk menampilkan nilai secara independen dari kueri apa pun.
Daniel Vérité
61

Satu kata terakhir pada variabel PSQL:

  1. Mereka tidak berkembang jika Anda menyertakannya dalam tanda kutip tunggal dalam pernyataan SQL. Jadi ini tidak berhasil:

    SELECT * FROM foo WHERE bar = ':myvariable'
  2. Untuk memperluas ke string literal dalam pernyataan SQL, Anda harus menyertakan tanda kutip dalam set variabel. Namun, nilai variabel sudah harus dilampirkan dalam tanda kutip, yang berarti bahwa Anda memerlukan set tanda kutip kedua , dan set dalam harus diloloskan. Maka Anda membutuhkan:

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

    EDIT : dimulai dengan PostgreSQL 9.1, Anda dapat menulis sebagai gantinya:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
Craig Walker
sumber
12
Cheers for:'myvariable'
Andomar
Persis apa yang saya cari!
KeniSteward
47

Anda dapat mencoba menggunakan klausa DENGAN .

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
sumber
Cara ini sebagian besar nyaman ketika Anda menggunakan nilai perhitungan yang sama beberapa kali dalam permintaan Anda.
skaurus
2
Berlawanan dengan laporan Bryce, sepertinya itu baik untukku. 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; Ouputs usia Jack dan Jack, seperti yang diharapkan.
Joshua
2
Untuk banyak kegunaan, terutama dalam konteks kerangka kerja aplikasi web seperti Python Flask, ini adalah solusi terbaik untuk menggunakan kembali nilai yang dihitung kompleks dalam satu permintaan.
Will
1
Adakah yang bisa menyarankan bagaimana ini bisa berfungsi dalam sebuah insert?
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

Khusus untuk psql, Anda dapat melewatkan psqlvariabel dari baris perintah juga; Anda dapat melewati mereka -v. Berikut ini contoh penggunaan:

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

Perhatikan bahwa titik dua tidak dikutip, maka nama variabelnya dikutip. Sintaks aneh, saya tahu. Ini hanya berfungsi di psql; itu tidak akan bekerja di (katakanlah) PgAdmin-III.

Substitusi ini terjadi selama pemrosesan input dalam psql, jadi Anda tidak dapat (katakanlah) mendefinisikan fungsi yang menggunakan :'filepath'dan mengharapkan nilai :'filepath'untuk berubah dari sesi ke sesi. Ini akan diganti sekali, ketika fungsi didefinisikan, dan kemudian akan menjadi konstan setelah itu. Ini berguna untuk scripting tetapi tidak digunakan runtime.

Craig Ringer
sumber
variabel psql, misalnya: 'filepath', Anda menunjukkan: "Perhatikan bahwa titik dua tidak dikutip, maka nama variabel sendiri dikutip." Terima kasih! Kamu! Saya sudah meletakkan banyak penyok berbentuk dahi ke meja saya mencoba untuk membuat ini bekerja, dan Anda hanya menghemat satu ton lagi. Persis seperti yang saya butuhkan untuk beberapa skrip.
Jason
13

FWIW, masalah sebenarnya adalah bahwa saya telah menyertakan tanda titik koma di akhir perintah \ set saya:

\ set owner_password 'thepassword';

Titik koma ditafsirkan sebagai karakter aktual dalam variabel:

\ echo: owner_password kata sandi;

Jadi ketika saya mencoba menggunakannya:

BUAT PERAN myrole LOGIN TIDAK TERKENAL PASSWORD: owner_password NOINHERIT CREATEDB CREATEROLE VALID SAMPAI 'infinity';

...Saya mengerti:

BUAT PERAN myrole LOGIN PASSWORD TAK TERBATAS kata sandi; NOINHERIT CREATEDB CREATEROLE VALID SAMPAI 'infinity';

Itu tidak hanya gagal untuk mengatur tanda kutip di sekitar literal, tetapi membagi perintah menjadi 2 bagian (yang kedua tidak valid karena dimulai dengan "NOINHERIT").

Moral dari cerita ini: "variabel" PostgreSQL benar-benar makro yang digunakan dalam ekspansi teks, bukan nilai sebenarnya. Saya yakin itu berguna, tetapi pada awalnya sulit.

Craig Walker
sumber
12

Anda perlu menggunakan salah satu bahasa prosedural seperti PL / pgSQL bukan bahasa proc SQL. Dalam PL / pgSQL Anda dapat menggunakan vars tepat di pernyataan SQL. Untuk kutipan tunggal, Anda dapat menggunakan fungsi literal kutipan.


sumber
5
Itu tidak bisa dilakukan di postgres itu sendiri, tetapi itu bisa dilakukan di aplikasi klien PSQL.
Philluminati
1
plpgsql dapat (sekarang) digunakan di postgres (sejak versi 9.0)) postgresql.org/docs/9.0/static/sql-do.html
Jasen
11

postgres (sejak versi 9.0) memungkinkan blok anonim dalam bahasa skrip sisi server yang didukung

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

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

Karena semuanya ada di dalam string, variabel string eksternal yang disubstitusi harus diloloskan dan dikutip dua kali. Sebaliknya, menggunakan kutipan dolar tidak akan memberikan perlindungan penuh terhadap injeksi SQL.

Jasen
sumber
5

Pendekatan lain adalah (ab) menggunakan mekanisme GUC PostgreSQL untuk membuat variabel. Lihat jawaban sebelumnya untuk detail dan contoh.

Anda mendeklarasikan GUC di postgresql.conf, lalu ubah nilainya saat runtime dengan SETperintah dan dapatkan nilainya dengan current_setting(...).

Saya tidak merekomendasikan ini untuk penggunaan umum, tetapi bisa berguna dalam kasus-kasus sempit seperti yang disebutkan dalam pertanyaan terkait, di mana poster menginginkan cara untuk menyediakan nama pengguna tingkat aplikasi untuk memicu dan fungsi.

Craig Ringer
sumber
4

Saya memecahkannya dengan tabel temp.

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

Dengan cara ini, saya memiliki "variabel" yang dapat saya gunakan pada beberapa kueri, yang unik untuk sesi ini. Saya membutuhkannya untuk menghasilkan "nama pengguna" yang unik sementara masih tidak memiliki tabrakan jika mengimpor pengguna dengan nama pengguna yang sama.

geon
sumber
Ini tampaknya menjadi satu-satunya cara yang berfungsi dalam alat visual seperti Heidi SQL.
Altair7852
2

Saya menemukan pertanyaan ini dan jawabannya sangat berguna, tetapi juga membingungkan. Saya memiliki banyak masalah dalam membuat variabel yang dikutip berfungsi, jadi inilah cara saya membuatnya berfungsi:

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

Dengan cara ini Anda dapat mendefinisikan variabel dalam satu pernyataan. Saat Anda menggunakannya, tanda kutip tunggal akan dimasukkan ke dalam variabel.

CATATAN! Ketika saya memberikan komentar setelah variabel yang dikutip itu tersedot sebagai bagian dari variabel ketika saya mencoba beberapa metode dalam jawaban lain. Itu benar-benar mengacaukan saya untuk sementara waktu. Dengan metode ini, komentar tampaknya diperlakukan seperti yang Anda harapkan.

Nate
sumber
\set deployment_pass 'string_password' ALTER USER :deployment_user WITH PASSWORD :'deployment_pass';
Jasen
\ set bukan SQL itu adalah perintah built-in psql sql komentar tidak didukung.
Jasen
2

Saya sangat merindukan fitur itu. Satu-satunya cara untuk mencapai sesuatu yang serupa adalah menggunakan fungsi.

Saya telah menggunakannya dalam dua cara:

  • fungsi perl yang menggunakan variabel $ _SHARED
  • simpan variabel Anda dalam tabel

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

Versi tabel:

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

Catatan:

  • plperlu lebih cepat dari perl
  • pg_backend_pid bukan identifikasi sesi terbaik, pertimbangkan untuk menggunakan pid yang dikombinasikan dengan backend_start dari pg_stat_activity
  • versi tabel ini juga buruk karena Anda harus menghapus ini sesekali (dan tidak menghapus variabel sesi yang sedang bekerja)
Kaiko Kaur
sumber
1

Variabel dalam psql mengisap. Jika Anda ingin mendeklarasikan bilangan bulat, Anda harus memasukkan bilangan bulat, lalu lakukan carriage return, lalu akhiri pernyataan dalam tanda titik koma. Mengamati:

Katakanlah saya ingin mendeklarasikan variabel integer my_vardan memasukkannya ke dalam tabel test:

Tabel contoh 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)

Jelas, belum ada di tabel ini:

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

Kami mendeklarasikan variabel. Perhatikan bagaimana titik koma ada di baris berikutnya!

thedatabase=# \set my_var 999
thedatabase=# ;

Sekarang kita bisa memasukkan. Kita harus menggunakan :''sintaks yang terlihat aneh ini :

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

Berhasil!

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

Penjelasan:

Jadi ... apa yang terjadi jika kita tidak memiliki titik koma di baris berikutnya? Variabelnya? Lihat:

Kami menyatakan my_vartanpa garis baru.

thedatabase=# \set my_var 999;

Ayo pilih my_var.

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

WTF apakah itu? Ini bukan bilangan bulat , ini sebuah string 999; !

thedatabase=# select 999;
 ?column? 
----------
      999
(1 row)
Nama tampilan
sumber
5
Alasan titik koma melakukan hal-hal yang tidak terduga untuk Anda adalah bahwa titik koma mengakhiri pernyataan SQL, tetapi Anda mengetikkan perintah psql, \ set, yang bukan SQL dan TIDAK mengambil tanda titik koma. Menempatkan titik koma di baris berikutnya tidak akan sakit, tetapi tidak melakukan apa pun. Itu pernyataan kosong.
volkerk
1

Saya telah memposting solusi baru untuk ini di utas lain .

Ini menggunakan tabel untuk menyimpan variabel, dan dapat diperbarui kapan saja. Fungsi pengambil statis yang tidak bergerak dibuat secara dinamis (oleh fungsi lain), dipicu oleh pembaruan ke tabel Anda. Anda mendapatkan penyimpanan meja yang bagus, ditambah kecepatan cepat dari pengambil yang tidak berubah.

Brev
sumber