PostgreSQL: Bagaimana cara melewatkan parameter dari baris perintah?

95

Saya memiliki kueri yang agak rinci dalam skrip yang menggunakan ?placeholder. Saya ingin menguji kueri yang sama ini langsung dari baris perintah psql (di luar skrip). Saya ingin menghindari masuk dan mengganti semua ?dengan nilai aktual, sebagai gantinya saya ingin meneruskan argumen setelah kueri.

Contoh:

SELECT  * 
FROM    foobar
WHERE   foo = ?
   AND  bar = ?
    OR  baz = ?  ;

Mencari sesuatu seperti:

%> {select * from foobar where foo=? and bar=? or baz=? , 'foo','bar','baz' };
vol7ron
sumber
Tolong, lebih banyak konteks. Apakah kueri ini dalam file SQL, di Perl / Python / Ruby / <masukkan bahasa skrip favorit di sini> skrip, atau di tempat lain?
@ Jack: Saya ingin melakukan ini langsung dari psql prompt (baris perintah). Saya mengambil kode saya dari skrip, tetapi tidak ingin melalui seluruh proses temukan / ganti.
vol7ron
@ Vol7ron, silakan lihat jawaban saya di bawah ini untuk contoh baris perintah psql.
MAbraham1
1
@ MAbraham1: bagus. Saya seharusnya memberikan lebih banyak latar belakang untuk pertanyaan saya. Saya memiliki banyak skrip yang memiliki SQL dalam teks terbuka. Terkadang berguna untuk mengambilnya dan menekannya langsung ke database, dengan nilai kustom untuk debugging. Saya sedang mencari cara untuk melakukannya dengan mudah di dalam Postgres tanpa perlu menyimpan file tambahan.
vol7ron
@ Vol7ron, terima kasih. Saya berpikir dalam hal pekerjaan batch, namun Anda juga harus dapat menggunakan token dalam SQL terbuka. Jangan lupa untuk memilih jika Anda menyukai jawaban saya.
MAbraham1

Jawaban:

181

Anda dapat menggunakan konstruksi -v misalnya

psql -v v1=12  -v v2="'Hello World'" -v v3="'2010-11-12'"

dan kemudian merujuk ke variabel di sql sebagai: v1,: v2 dll

select * from table_1 where id = :v1;

Harap perhatikan bagaimana kami melewatkan nilai string / tanggal menggunakan dua tanda kutip " '...' "

Gavin
sumber
2
+1 Argumen yang menarik dan lewat. Apakah Anda mengetahui cara untuk melakukan ini setelah masuk?
vol7ron
9
Tentu, gunakan saja \set v3 'another value'. Ingat saja, ketika Anda perlu mengutip nilai dalam pernyataan SQL, gunakan apostrof di sekitar nama variabel, seperti ini:SELECT * FROM foo WHERE bar = :'v3';
Cromax
2
Saya kira mereka mendapatkannya dariawk
Neil McGuigan
1
Dapat @ digunakan sebagai pengganti: seperti sqlserver
Awais Mahmood
5
Catatan, setelah membaca ini saya berharap menemukan bahwa variabel yang disetel dengan -v akan tersedia untuk perintah yang dieksekusi dengan -c, tetapi, sayangnya tidak. Dengan kata lain, psql -v v1=12 -v v2="'Hello World'" -v v3="'2010-11-12'" -c 'select * from table_1 where id = :v1;' akan menghasilkan kesalahan sintaksis. Namun, jika bash adalah cangkang Anda, Anda dapat mencoba: psql -v v1=12 -v v2="'Hello World'" -v v3="'2010-11-12'" <<< 'select * from table_1 where id = :v1;' untuk mendapatkan hasil yang baik.
malcook
31

Ditemukan di PostgreSQL, Anda dapat membuat PREPAREpernyataan seperti yang Anda bisa dalam bahasa scripting. Sayangnya, Anda tetap tidak dapat menggunakan ?, tetapi Anda dapat menggunakan $nnotasi.

Menggunakan contoh di atas:

PREPARE foo(text,text,text) AS
    SELECT  * 
    FROM    foobar
    WHERE   foo = $1
       AND  bar = $2
        OR  baz = $3  ;
EXECUTE foo('foo','bar','baz');
DEALLOCATE foo;
vol7ron
sumber
@IvanBlack apakah ada hal lain yang ingin Anda masukkan dengan itu? :) deallocation secara otomatis dilakukan pada akhir sesi
vol7ron
Perhatikan saja bahwa sekarang foosedang sibuk dan yang lain PREPAREharus memiliki nama lain sementara sesi saat ini tidak ditutup. Jika Anda bermain dengan PREPAREke psqlsulit untuk menemukan setiap kali nama baru dan DEALLOCATEdapat membantu dengan itu =)
Ivan Hitam
Terima kasih telah menyebutkan ini. Saya belum pernah menggunakan PREPARE dalam beberapa waktu, tapi itu adalah informasi yang berguna
vol7ron
Solusi ini IMO sangat bagus. Efek samping yang berguna - Anda dapat dengan mudah memanggil pernyataan yang sudah disiapkan beberapa kali.
Yuri
14

Dalam psql ada mekanisme melalui

\set name val

perintah, yang seharusnya terkait dengan -v name=valopsi baris perintah. Mengutip itu menyakitkan, Dalam banyak kasus, lebih mudah untuk meletakkan seluruh query daging di dalam shell here-document.

Edit

Ups, saya seharusnya mengatakan -valih-alih -P(yang untuk opsi pemformatan) balasan sebelumnya sudah benar.

wildplasser
sumber
7

Anda juga bisa memasukkan parameter di baris perintah psql, atau dari file batch. Pernyataan pertama mengumpulkan detail yang diperlukan untuk menghubungkan ke database Anda.

Prompt terakhir menanyakan nilai kendala, yang akan digunakan dalam klausa kolom WHERE IN (). Ingatlah untuk mengutip tunggal jika string, dan pisahkan dengan koma:

@echo off
echo "Test for Passing Params to PGSQL"
SET server=localhost
SET /P server="Server [%server%]: "

SET database=amedatamodel
SET /P database="Database [%database%]: "

SET port=5432
SET /P port="Port [%port%]: "

SET username=postgres
SET /P username="Username [%username%]: "

SET /P bunos="Enter multiple constraint values for IN clause [%constraints%]: "
ECHO you typed %constraints%
PAUSE
REM pause
"C:\Program Files\PostgreSQL\9.0\bin\psql.exe" -h %server% -U %username% -d %database% -p %port% -e -v v1=%constraints% -f test.sql

Sekarang di file kode SQL Anda, tambahkan token v1 di dalam klausa WHERE Anda, atau di mana pun di SQL. Perhatikan bahwa token juga dapat digunakan dalam pernyataan SQL terbuka, tidak hanya dalam file. Simpan ini sebagai test.sql:

SELECT * FROM myTable
WHERE NOT someColumn IN (:v1);

Di Windows, simpan seluruh file sebagai file DOS BATch (.bat), simpan test.sql di direktori yang sama, dan luncurkan file batch.

Terima kasih untuk Dave Page, dari EnterpriseDB, untuk skrip asli yang diminta.

MAbraham1
sumber
+1 untuk contoh Windows; meskipun sebagian besar database Pg ada dalam varian * nix
vol7ron
2

Tampaknya yang Anda minta tidak dapat dilakukan langsung dari baris perintah . Anda juga harus menggunakan fungsi yang ditentukan pengguna di plpgsql atau memanggil kueri dari bahasa skrip (dan pendekatan terakhir membuatnya sedikit lebih mudah untuk menghindari injeksi SQL).

Komunitas
sumber
Bukan saya - berkali-kali saya berharap bahwa suara negatif harus memerlukan semacam penjelasan (mirip dengan alasan mengapa kami memilih untuk menutup pertanyaan), bahkan jika dibiarkan tanpa nama.
vol7ron
0

Saya ingin menawarkan jawaban lain yang terinspirasi oleh komentar @ malcook (menggunakan bash).

Opsi ini dapat bekerja untuk Anda jika Anda perlu menggunakan variabel shell dalam kueri Anda saat menggunakan -cbendera . Secara khusus, saya ingin mendapatkan hitungan tabel, yang namanya adalah variabel shell (yang tidak dapat Anda lewati secara langsung saat menggunakan -c).

Asumsikan Anda memiliki variabel shell Anda

$TABLE_NAME='users'

Kemudian Anda bisa mendapatkan hasilnya dengan menggunakan

psql -q -A -t -d databasename -c <<< echo "select count(*) from $TABLE_NAME;"

( -q -A -tini hanya untuk mencetak nomor yang dihasilkan tanpa format tambahan)

Saya akan mencatat bahwa echodi sini-string ( <<<operator) mungkin tidak diperlukan, awalnya saya pikir kutipan itu sendiri akan baik-baik saja, mungkin seseorang dapat menjelaskan alasannya.

Broper
sumber
0

Saya akhirnya menggunakan versi jawaban @ vol7ron yang lebih baik:

DO $$
BEGIN
    IF NOT EXISTS(SELECT 1 FROM pg_prepared_statements WHERE name = 'foo') THEN
        PREPARE foo(text,text,text) AS
            SELECT  * 
            FROM    foobar
            WHERE   foo = $1
                AND bar = $2
                OR  baz = $3;
    END IF;
END$$;
EXECUTE foo('foo','bar','baz');

Dengan cara ini Anda selalu dapat mengeksekusinya dalam urutan ini (kueri disiapkan hanya jika belum disiapkan), ulangi eksekusi dan dapatkan hasil dari kueri terakhir.

Konard
sumber