คุณใช้ตัวแปรสคริปต์ใน psql ได้อย่างไร?


134

ใน MS SQL Server ฉันสร้างสคริปต์ของฉันเพื่อใช้ตัวแปรที่ปรับแต่งได้:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

จากนั้นฉันจะเปลี่ยนค่าของ@somevariableรันไทม์ขึ้นอยู่กับค่าที่ฉันต้องการในสถานการณ์นั้น ๆ เนื่องจากอยู่ด้านบนของสคริปต์จึงง่ายต่อการดูและจดจำ

ฉันจะทำเช่นเดียวกันกับไคลเอนต์ PostgreSQL ได้psqlอย่างไร


5
FWIW ตัวดำเนินการ \ set ดูเหมือนจะเกี่ยวข้องกับเครื่องมือบรรทัดคำสั่ง psql ไม่ใช่กับภาษาแบทช์ pgsql ฉันอาจจะผิด
Daniel Yankowsky

1
คุณใช้ Postgres เวอร์ชันใด
Kuberchaun

คำตอบ:


181

ตัวแปร Postgres ถูกสร้างผ่านคำสั่ง \ set ตัวอย่างเช่น ...

\set myvariable value

... และสามารถแทนที่ได้เช่น ...

SELECT * FROM :myvariable.table1;

... หรือ ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

แก้ไข: ใน psql 9.1 ตัวแปรสามารถขยายได้ในเครื่องหมายคำพูดเช่น:

\set myvariable value 

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

ในไคลเอนต์ psql เวอร์ชันเก่า:

... ถ้าคุณต้องการใช้ตัวแปรเป็นค่าในการสืบค้นสตริงเงื่อนไขเช่น ...

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

... จากนั้นคุณต้องใส่เครื่องหมายคำพูดในตัวแปรเองเนื่องจากข้างต้นจะไม่ทำงาน แทนที่จะกำหนดตัวแปรของคุณเช่นนี้ ...

\set myvariable 'value'

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

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

ตอนนี้คุณมีทั้งตัวแปรที่ยกมาและไม่มีเครื่องหมายคำพูดของสตริงเดียวกัน! และคุณสามารถทำสิ่งนี้ได้ ....

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;

67
\setใช้สำหรับpsqlเครื่องมือเท่านั้นคุณไม่สามารถใช้งานได้ในขั้นตอนการจัดเก็บ!
sorin

6
@SorinSbarnea OP ถามเกี่ยวกับสคริปต์ไม่ใช่ขั้นตอน
Daniel Serodio

35
คำตอบนี้ผสมผสานpsqlคำสั่ง meta \setกับคำสั่ง PostgreSQL ในแบบที่สับสน
Erwin Brandstetter

20
ตั้งแต่ postgresql 9.1 ใน psql ตอนนี้คุณสามารถใช้: 'variable' เพื่อกำหนดให้เป็นค่าที่เหมาะสมสำหรับคุณหรือ: "variable" เพื่อใช้เป็นตัวระบุ
HitScan

9
\set myvariable 'value'ไม่ได้รวมถึงคำพูดใด ๆ ภายในตัวแปรตรงกันข้ามกับสิ่งที่พูดว่าคำตอบนี้ หากมีข้อสงสัยให้ใช้\echo :myvariableภายใน psql เพื่อแสดงค่าโดยอิสระของแบบสอบถามใด ๆ
Daniel Vérité

61

คำสุดท้ายของตัวแปร PSQL:

  1. พวกเขาจะไม่ขยายหากคุณใส่ไว้ในเครื่องหมายคำพูดเดี่ยวในคำสั่ง SQL ดังนั้นจึงไม่ได้ผล:

    SELECT * FROM foo WHERE bar = ':myvariable'
  2. หากต้องการขยายเป็นสตริงลิเทอรัลในคำสั่ง SQL คุณต้องรวมเครื่องหมายคำพูดไว้ในชุดตัวแปร อย่างไรก็ตามค่าตัวแปรจะต้องอยู่ในเครื่องหมายคำพูดอยู่แล้วซึ่งหมายความว่าคุณต้องมีเครื่องหมายคำพูดชุดที่สองและชุดภายในจะต้องมีค่า Escape ดังนั้นคุณต้อง:

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

    แก้ไข : เริ่มต้นด้วย PostgreSQL 9.1 คุณสามารถเขียนแทน:

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

12
Cheers for:'myvariable'
Andomar

สิ่งที่ฉันกำลังมองหา!
KeniSteward

47

คุณสามารถพยายามที่จะใช้กับประโยค

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;

วิธีนี้ส่วนใหญ่จะสะดวกเมื่อคุณใช้ค่าที่คำนวณเดียวกันสองสามครั้งในแบบสอบถาม
สการัส

2
ตรงกันข้ามกับรายงานของ Bryce ดูเหมือนว่าจะทำงานได้ดีสำหรับฉัน 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 Jack และอายุของ Jack ตามที่คาดไว้
Joshua

2
สำหรับการใช้งานจำนวนมากโดยเฉพาะอย่างยิ่งในบริบทของเฟรมเวิร์กเว็บแอปพลิเคชันเช่น Python Flask นี่เป็นทางออกที่ดีที่สุดสำหรับการนำค่าจากการคำนวณที่ซับซ้อนกลับมาใช้ใหม่ภายในแบบสอบถามเดียว
Will

1
ใครช่วยแนะนำวิธีการนี้ในส่วนแทรกได้บ้าง
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

โดยเฉพาะpsqlคุณสามารถส่งผ่านpsqlตัวแปรจากบรรทัดคำสั่งได้เช่นกัน คุณสามารถผ่านมันไป-vได้ นี่คือตัวอย่างการใช้งาน:

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

โปรดทราบว่าเครื่องหมายจุดคู่จะไม่มีเครื่องหมายคำพูดจากนั้นชื่อตัวแปรจะถูกยกมา ฉันรู้ไวยากรณ์แปลก ๆ ใช้งานได้ใน psql เท่านั้น มันจะไม่ทำงานใน (พูด) PgAdmin-III

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


ตัวแปร psql เช่น 'filepath' คุณได้ชี้ให้เห็น: "โปรดทราบว่าเครื่องหมายจุดคู่จะไม่มีเครื่องหมายคำพูดจากนั้นชื่อตัวแปรจะถูกยกมา" ขอบคุณ! คุณ! ฉันใส่รอยบุบที่เป็นรูปหน้าผากไว้ในโต๊ะแล้วเพื่อพยายามทำงานนี้และคุณก็ช่วยฉันได้มากขึ้น สิ่งที่ฉันต้องการสำหรับการเขียนสคริปต์
Jason

13

FWIW ปัญหาที่แท้จริงคือฉันได้ใส่อัฒภาคไว้ท้ายคำสั่ง \ set ของฉัน:

\ set owner_password 'รหัสผ่าน';

อัฒภาคถูกตีความว่าเป็นอักขระจริงในตัวแปร:

\ echo: owner_password รหัสผ่าน;

ดังนั้นเมื่อฉันพยายามใช้มัน:

สร้าง ROLE myrole LOGIN UNENCRYPTED PASSWORD: owner_password NOINHERIT CREATEDB CREATEROLE ที่ใช้ได้จนถึง 'infinity';

... ฉันได้รับสิ่งนี้:

สร้าง ROLE myrole เข้าสู่ระบบ UNENCRYPTED PASSWORD รหัสผ่าน; NOINHERIT CREATEDB CREATEROLE ใช้ได้จนถึง 'อินฟินิตี้';

ซึ่งไม่เพียงล้มเหลวในการตั้งเครื่องหมายคำพูดรอบตัวอักษร แต่ยังแบ่งคำสั่งออกเป็น 2 ส่วน (ส่วนที่สองไม่ถูกต้องเมื่อเริ่มต้นด้วย "NOINHERIT")

คุณธรรมของเรื่องนี้: "ตัวแปร" ของ PostgreSQL เป็นมาโครที่ใช้ในการขยายข้อความไม่ใช่ค่าที่แท้จริง ฉันแน่ใจว่ามีประโยชน์ แต่ตอนแรกมันยุ่งยาก


12

คุณต้องใช้ภาษาขั้นตอนใดภาษาหนึ่งเช่น PL / pgSQL ไม่ใช่ภาษา SQL proc ใน PL / pgSQL คุณสามารถใช้ vars ได้ทันทีในคำสั่ง SQL สำหรับคำพูดเดี่ยวคุณสามารถใช้ฟังก์ชันลิเทอรัลใบเสนอราคา


5
ไม่สามารถทำได้ใน postgres เอง แต่สามารถทำได้ในแอปพลิเคชันไคลเอนต์ PSQL
Philluminati

1
plpgsql สามารถ (ตอนนี้) ใช้ใน postgres (ตั้งแต่เวอร์ชัน 9.0)) postgresql.org/docs/9.0/static/sql-do.html
Jasen

11

postgres (ตั้งแต่เวอร์ชัน 9.0) อนุญาตให้บล็อกแบบไม่ระบุชื่อในภาษาสคริปต์ฝั่งเซิร์ฟเวอร์ที่รองรับ

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

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

เนื่องจากทุกอย่างอยู่ในสตริงตัวแปรสตริงภายนอกที่ถูกแทนที่จะต้องมีการ Escape และยกมาสองครั้ง การใช้การเสนอราคาดอลลาร์แทนจะไม่สามารถป้องกันการแทรก SQL ได้อย่างเต็มที่


5

อีกแนวทางหนึ่งคือ (ab) ใช้กลไก PostgreSQL GUC เพื่อสร้างตัวแปร ดูคำตอบก่อนหน้านี้สำหรับรายละเอียดและตัวอย่าง

คุณประกาศ GUC ในpostgresql.confจากนั้นเปลี่ยนค่าที่รันไทม์ด้วยSETคำสั่งและรับค่าด้วยcurrent_setting(...).

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


4

ฉันแก้ไขมันด้วยตารางชั่วคราว

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

ด้วยวิธีนี้ฉันมี "ตัวแปร" ที่สามารถใช้กับข้อความค้นหาหลายรายการซึ่งไม่ซ้ำกันสำหรับเซสชัน ฉันต้องการมันเพื่อสร้าง "ชื่อผู้ใช้" ที่ไม่ซ้ำกันในขณะที่ยังไม่มีการชนกันหากนำเข้าผู้ใช้ที่มีชื่อผู้ใช้เดียวกัน


ดูเหมือนจะเป็นวิธีการทำงานเดียวในเครื่องมือภาพเช่น Heidi SQL
Altair7852

2

ฉันพบว่าคำถามนี้และคำตอบมีประโยชน์มาก แต่ก็ทำให้สับสน ฉันมีปัญหามากมายในการทำให้ตัวแปรที่ยกมาทำงานดังนั้นนี่คือวิธีที่ฉันทำให้มันได้ผล:

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

วิธีนี้คุณสามารถกำหนดตัวแปรในคำสั่งเดียว เมื่อคุณใช้คำพูดเดี่ยวจะถูกฝังลงในตัวแปร

บันทึก! เมื่อฉันใส่ความคิดเห็นหลังตัวแปรที่ยกมามันถูกดูดเข้าไปเป็นส่วนหนึ่งของตัวแปรเมื่อฉันลองใช้วิธีการบางอย่างในคำตอบอื่น ๆ นั่นทำให้ฉันคาดคั้นจริงๆ ด้วยวิธีนี้ความคิดเห็นดูเหมือนจะได้รับการปฏิบัติตามที่คุณคาดหวัง


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

\ set ไม่ใช่ SQL ซึ่งเป็นคำสั่งในตัวของ psql ไม่รองรับความคิดเห็น sql
Jasen

2

ฉันคิดถึงฟีเจอร์นั้นจริงๆ วิธีเดียวที่จะบรรลุสิ่งที่คล้ายกันคือการใช้ฟังก์ชัน

ฉันใช้มันสองวิธี:

  • ฟังก์ชัน perl ที่ใช้ตัวแปร $ _SHARED
  • จัดเก็บตัวแปรของคุณในตาราง

เวอร์ชัน 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;

เวอร์ชันตาราง:

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

หมายเหตุ:

  • plperlu เร็วกว่า perl
  • pg_backend_pid ไม่ใช่การระบุเซสชันที่ดีที่สุดให้พิจารณาใช้ pid ร่วมกับ backend_start จาก pg_stat_activity
  • เวอร์ชันตารางนี้ไม่ดีเช่นกันเนื่องจากคุณต้องล้างสิ่งนี้เป็นครั้งคราว (และไม่ลบตัวแปรเซสชันที่ทำงานอยู่ในปัจจุบัน)

1

ตัวแปรในการpsqlดูด หากคุณต้องการประกาศจำนวนเต็มคุณต้องป้อนจำนวนเต็มจากนั้นทำการส่งคืนค่าขนส่งจากนั้นจบคำสั่งด้วยเครื่องหมายอัฒภาค สังเกต:

สมมติว่าฉันต้องการประกาศตัวแปรจำนวนเต็มmy_varและแทรกลงในตารางtest:

ตัวอย่างตาราง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)

เห็นได้ชัดว่ายังไม่มีอะไรในตารางนี้:

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

เราประกาศตัวแปร สังเกตว่าอัฒภาคอยู่ในบรรทัดถัดไปอย่างไร!

thedatabase=# \set my_var 999
thedatabase=# ;

ตอนนี้เราสามารถแทรก เราต้องใช้:''ไวยากรณ์ที่ดูแปลก ๆ นี้:

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

มันได้ผล!

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

คำอธิบาย:

จะเกิดอะไรขึ้นถ้าเราไม่มีอัฒภาคในบรรทัดถัดไป? ตัวแปร? ดู:

เราประกาศmy_varโดยไม่ขึ้นบรรทัดใหม่

thedatabase=# \set my_var 999;

มาเลือกmy_varกันเลย

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

WTF คืออะไร? ไม่ใช่จำนวนเต็มแต่เป็นสตริง 999; !

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

5
เหตุผลที่เซมิโคลอนทำสิ่งที่ไม่คาดคิดให้คุณคืออัฒภาคยุติคำสั่ง SQL แต่คุณกำลังพิมพ์คำสั่ง psql, \ set ซึ่งไม่ใช่ SQL และไม่ใช้เครื่องหมายอัฒภาคสิ้นสุด การใส่อัฒภาคในบรรทัดถัดไปจะไม่เจ็บ แต่ไม่ได้ทำอะไรเลย มันเป็นคำสั่งที่ว่างเปล่า
volkerk

1

ฉันได้โพสต์วิธีแก้ปัญหาใหม่ในหัวข้ออื่นแล้ว

ใช้ตารางเพื่อจัดเก็บตัวแปรและสามารถอัปเดตได้ตลอดเวลา ฟังก์ชัน getter คงที่ไม่เปลี่ยนรูปถูกสร้างขึ้นแบบไดนามิก (โดยฟังก์ชันอื่น) ทริกเกอร์โดยการอัปเดตตารางของคุณ คุณจะได้รับพื้นที่เก็บข้อมูลบนโต๊ะที่ดีบวกกับความเร็วที่รวดเร็วอย่างเห็นได้ชัดของตัวรับที่ไม่เปลี่ยนรูป

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