PostgreSQL: จะส่งผ่านพารามิเตอร์จากบรรทัดคำสั่งได้อย่างไร


95

ฉันมีคำถามที่ค่อนข้างละเอียดในสคริปต์ที่ใช้?ตัวยึดตำแหน่ง ฉันต้องการทดสอบแบบสอบถามเดียวกันนี้โดยตรงจากบรรทัดคำสั่ง psql (นอกสคริปต์) ฉันต้องการหลีกเลี่ยงการเข้าไปแทนที่?ค่าจริงทั้งหมด แต่ฉันต้องการส่งผ่านอาร์กิวเมนต์หลังแบบสอบถาม

ตัวอย่าง:

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

กำลังมองหาสิ่งที่ชอบ:

%> {select * from foobar where foo=? and bar=? or baz=? , 'foo','bar','baz' };

โปรดบริบทเพิ่มเติม คำค้นหานี้อยู่ในไฟล์ SQL ในสคริปต์ Perl / Python / Ruby / <insert favorite scripting language here> หรือในที่อื่น ๆ

@ แจ็ค: ฉันต้องการทำสิ่งนี้โดยตรงจากพรอมต์ psql (บรรทัดคำสั่ง) ฉันใช้รหัสของฉันจากสคริปต์ แต่ไม่ต้องการทำตามกระบวนการค้นหา / แทนที่ทั้งหมด
vol7ron

@ Vol7ron โปรดดูคำตอบของฉันด้านล่างสำหรับตัวอย่างบรรทัดคำสั่ง psql
MAbraham1

1
@ MAbraham1: ดี ฉันควรจะให้พื้นฐานเพิ่มเติมสำหรับคำถามของฉัน ฉันมีสคริปต์จำนวนมากที่มี SQL ในข้อความที่เปิดอยู่ บางครั้งก็มีประโยชน์ที่จะนำสิ่งเหล่านี้และตีโดยตรงกับฐานข้อมูลด้วยค่าที่กำหนดเองสำหรับการแก้ไข ฉันกำลังมองหาวิธีที่จะทำได้อย่างง่ายดายภายใน Postgres โดยไม่ต้องบันทึกไฟล์เพิ่มเติม
vol7ron

@ Vol7ron ขอบคุณ ฉันคิดในแง่ของงานแบตช์อย่างไรก็ตามคุณควรจะสามารถใช้โทเค็นใน SQL แบบเปิดได้เช่นกัน อย่าลืมโหวตถ้าคุณชอบคำตอบของฉัน
MAbraham1

คำตอบ:


181

คุณสามารถใช้โครงสร้าง -v เช่น

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

จากนั้นอ้างถึงตัวแปรใน sql เป็น: v1,: v2 เป็นต้น

select * from table_1 where id = :v1;

โปรดใส่ใจเกี่ยวกับวิธีที่เราส่งผ่านค่าสตริง / วันที่โดยใช้เครื่องหมายคำพูดสองคำ " '...' "


2
+1 ที่น่าสนใจส่งผ่านอาร์กิวเมนต์ที่มีชื่อ คุณทราบวิธีใดบ้างที่จะทำได้เมื่อเข้าสู่ระบบแล้ว?
vol7ron

9
\set v3 'another value'แน่นอนว่าการใช้งานเพียงแค่ เพียงจำไว้ว่าเมื่อคุณต้องการอ้างค่าในคำสั่ง SQL ให้ใช้เครื่องหมายอะพอสทรอฟีรอบชื่อตัวแปรเช่นนี้SELECT * FROM foo WHERE bar = :'v3';
Cromax

2
ฉันเดาว่าพวกเขาได้รับจากawk
Neil McGuigan

1
สามารถใช้ @ แทน: เช่น sqlserver
Awais Mahmood

5
หมายเหตุเมื่ออ่านสิ่งนี้ฉันหวังว่าจะพบว่าตัวแปรที่ตั้งค่าด้วย -v จะพร้อมใช้งานสำหรับคำสั่งที่ดำเนินการด้วย -c แต่อนิจจาพวกเขาไม่ใช่ กล่าวอีกนัยหนึ่ง psql -v v1=12 -v v2="'Hello World'" -v v3="'2010-11-12'" -c 'select * from table_1 where id = :v1;' จะสร้างข้อผิดพลาดทางไวยากรณ์ อย่างไรก็ตามหากทุบตีเป็นเปลือกของคุณคุณอาจลอง: psql -v v1=12 -v v2="'Hello World'" -v v3="'2010-11-12'" <<< 'select * from table_1 where id = :v1;' เพื่อให้ได้ผลดี
malcook

31

พบใน PostgreSQL คุณสามารถใช้PREPAREคำสั่งเช่นเดียวกับที่ทำได้ในภาษาสคริปต์ ขออภัยคุณยังไม่สามารถใช้งานได้?แต่คุณสามารถใช้$nสัญกรณ์ได้

โดยใช้ตัวอย่างข้างต้น:

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;

@IvanBlack มีอย่างอื่นที่คุณต้องการรวมไว้ด้วยหรือไม่? :) deallocation จะดำเนินการโดยอัตโนมัติเมื่อสิ้นสุดเซสชัน
vol7ron

โปรดทราบว่าขณะนี้fooไม่ว่างและอีกรายการPREPAREควรมีชื่ออื่นในขณะที่เซสชันปัจจุบันยังไม่ปิด ถ้าคุณเล่นกับPREPAREเข้าpsqlก็ยากที่จะคิดค้นทุกครั้งที่มีชื่อใหม่และDEALLOCATEสามารถช่วยให้กับมัน =)
อีวานดำ

ขอขอบคุณที่กล่าวถึงสิ่งนี้ ฉันไม่ได้ใช้ PREPARE ในบางครั้ง แต่นั่นเป็นข้อมูลที่มีประโยชน์
vol7ron

โซลูชันนี้เป็น IMO ที่ดีมาก ผลข้างเคียงที่เป็นประโยชน์ - คุณสามารถเรียกใช้คำสั่งที่เตรียมไว้ได้หลายครั้ง
Yuri

14

ใน psql มีกลไกผ่านทาง

\set name val

คำสั่งซึ่งควรจะเชื่อมโยงกับ-v name=valตัวเลือกบรรทัดคำสั่ง การอ้างอิงเป็นเรื่องที่เจ็บปวดในกรณีส่วนใหญ่จะง่ายกว่าที่จะใส่เนื้อค้นหาทั้งหมดไว้ในเอกสารที่นี่

แก้ไข

โอ๊ะฉันควรจะพูด-vแทน-P(ซึ่งเป็นตัวเลือกการจัดรูปแบบ) คำตอบก่อนหน้านี้ทำให้ถูกต้อง


7

คุณยังสามารถส่งผ่านพารามิเตอร์ที่บรรทัดคำสั่ง psql หรือจากไฟล์แบตช์ คำสั่งแรกรวบรวมรายละเอียดที่จำเป็นสำหรับการเชื่อมต่อกับฐานข้อมูลของคุณ

พรอมต์สุดท้ายขอค่าข้อ จำกัด ซึ่งจะใช้ในคำสั่ง WHERE คอลัมน์ IN () อย่าลืมใส่เครื่องหมายอัญประกาศเดี่ยวหากสตริงและคั่นด้วยลูกน้ำ:

@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

ตอนนี้ในไฟล์โค้ด SQL ของคุณให้เพิ่มโทเค็น v1 ภายในส่วนคำสั่ง WHERE ของคุณหรือที่อื่น ๆ ใน SQL โปรดทราบว่าโทเค็นสามารถใช้ในคำสั่ง SQL แบบเปิดได้ไม่ใช่แค่ในไฟล์ บันทึกเป็น test.sql:

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

ใน Windows ให้บันทึกทั้งไฟล์เป็นไฟล์ DOS BATch (.bat) บันทึก test.sql ในไดเร็กทอรีเดียวกันและเปิดไฟล์แบตช์

ขอขอบคุณสำหรับ Dave Page จาก EnterpriseDB สำหรับสคริปต์ที่ได้รับแจ้งดั้งเดิม


+1 สำหรับตัวอย่าง Windows แม้ว่าฐานข้อมูล Pg ส่วนใหญ่จะอยู่ในตัวแปร * nix
vol7ron

2

ก็ปรากฏว่าสิ่งที่คุณขอไม่สามารถทำได้โดยตรงจากบรรทัดคำสั่ง คุณจะต้องใช้ฟังก์ชันที่ผู้ใช้กำหนดเองใน plpgsql หรือเรียกการสืบค้นจากภาษาสคริปต์ (และวิธีการหลังนี้ทำให้หลีกเลี่ยงการแทรก SQL ได้ง่ายขึ้นเล็กน้อย)


ไม่ใช่ฉัน - หลายครั้งฉันหวังว่าการโหวตลดควรต้องการคำอธิบายบางอย่าง (คล้ายกับเหตุผลที่เราโหวตให้ปิดคำถาม) แม้ว่าจะปล่อยทิ้งไว้โดยไม่ระบุชื่อก็ตาม
vol7ron

0

ฉันต้องการเสนอคำตอบอื่นที่ได้รับแรงบันดาลใจจากความคิดเห็นของ @ malcook (โดยใช้ bash)

ตัวเลือกนี้อาจใช้ได้ผลสำหรับคุณหากคุณจำเป็นต้องใช้ตัวแปรเชลล์ภายในแบบสอบถามของคุณเมื่อใช้-cแฟล็ก โดยเฉพาะฉันต้องการรับจำนวนตารางที่มีชื่อเป็นตัวแปรเชลล์ (ซึ่งคุณไม่สามารถส่งผ่านได้โดยตรงเมื่อใช้-c)

สมมติว่าคุณมีตัวแปรเชลล์ของคุณ

$TABLE_NAME='users'

จากนั้นคุณจะได้รับผลลัพธ์ของสิ่งนั้นโดยใช้

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

( -q -A -tเป็นเพียงการพิมพ์ตัวเลขผลลัพธ์โดยไม่ต้องจัดรูปแบบเพิ่มเติม)

ฉันจะสังเกตว่าechoในสตริงที่นี่ (ตัว<<<ดำเนินการ) อาจไม่จำเป็น แต่เดิมฉันคิดว่าเครื่องหมายคำพูดนั้นดีอาจมีใครบางคนสามารถชี้แจงเหตุผลของสิ่งนี้ได้


0

ฉันได้ใช้คำตอบ @ vol7ron เวอร์ชันที่ดีกว่า:

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

ด้วยวิธีนี้คุณสามารถดำเนินการตามลำดับนี้ได้ตลอดเวลา (แบบสอบถามที่เตรียมไว้เฉพาะในกรณีที่ยังไม่ได้เตรียมไว้) ให้ดำเนินการซ้ำและรับผลลัพธ์จากการสืบค้นล่าสุด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.