วิธีการประกาศตัวแปรในแบบสอบถาม PostgreSQL


241

ฉันจะประกาศตัวแปรสำหรับใช้ในแบบสอบถาม PostgreSQL 8.3 ได้อย่างไร

ใน MS SQL Server ฉันสามารถทำได้:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

ฉันจะทำเช่นเดียวกันใน PostgreSQL ได้อย่างไร ตามตัวแปรเอกสารจะประกาศเพียงว่า "ชื่อประเภท;" แต่นี่ทำให้ฉันมีข้อผิดพลาดทางไวยากรณ์:

myvar INTEGER;

ใครช่วยให้ฉันตัวอย่างของไวยากรณ์ที่ถูกต้องหรือไม่


2
มันสามารถทำได้ใน PostgreSQL เพียง ดูคำตอบสำหรับคำถามที่เกี่ยวข้องนี้: stackoverflow.com/questions/766657/…
Sean the Bean

2
คำตอบที่เกี่ยวข้องนี้มีคำตอบที่ดีกว่า: stackoverflow.com/questions/13316773/…
Erwin Brandstetter

คำตอบ:


113

ไม่มีคุณสมบัติดังกล่าวใน PostgreSQL คุณสามารถทำได้เฉพาะใน pl / PgSQL (หรือ pl / * อื่น ๆ ) แต่ไม่ใช่ใน SQL ธรรมดา

ข้อยกเว้นคือWITH ()แบบสอบถามที่สามารถทำงานเป็นตัวแปรหรือแม้กระทั่งtupleตัวแปร จะช่วยให้คุณส่งคืนตารางค่าชั่วคราว

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

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

228

ฉันบรรลุเป้าหมายเดียวกันโดยใช้WITHประโยคมันไม่มีที่ไหนเลยใกล้จะสวย แต่สามารถทำสิ่งเดียวกัน แม้ว่าตัวอย่างนี้มันเกินความจริงจริงๆ ฉันไม่แนะนำสิ่งนี้เป็นพิเศษ

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;

2
สิ่งนี้ใช้งานได้ดีสำหรับกรณีส่วนใหญ่ที่คุณต้องการตัวแปร อย่างไรก็ตามหากคุณต้องการใช้ตัวแปรสำหรับ LIMIT (ซึ่งไม่สามารถมีตัวแปรได้) คุณควรต้องการใช้\setตามคำแนะนำในคำตอบของ Shahriar Aghajani
cimmanon

1
สิ่งนี้เหมาะสำหรับเมื่อฉันมีสคริปต์การย้ายข้อมูลที่ฉันต้องการนำเข้าข้อมูลเชิงสัมพันธ์บางอย่าง เห็นได้ชัดว่าฉันจะไม่ทราบรหัสลำดับที่ได้รับข้อมูลเชิงสัมพันธ์
Relequestual

3
ฉันลองใช้วิธีนี้และพบวิธีที่ดีกว่า: JOIN myconstants ON trueจากนั้นไม่จำเป็นต้องเลือกตัวเลือกย่อย
vektor

7
ใช้งานได้เฉพาะในแบบสอบถามเดียวคุณไม่สามารถแบ่งปันWITHCTE ข้ามแบบสอบถามในธุรกรรม
Daenyth

2
คำถามเดิม WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;แต่ที่นี่นี่คือรูปแบบ: CROSS JOIN ซึ่งเป็นนิพจน์ตารางแบบแถวเดี่ยวทำซ้ำข้อมูลจริงสำหรับแถวทั้งหมดในตารางจริงและทำให้นิพจน์ง่ายขึ้น
Manngo

83

คุณสามารถลองใช้งานใน 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;

ข้างต้นต้องใช้ Postgres 9.0 หรือใหม่กว่า


1
คำสั่ง DO ถูกเพิ่มใน PostgreSQL 9.0 และไม่ทำงานใน 8.3
Johny

14
ใช้ CREATE TEMPORARY TABLE หรือ CREATE TEMP Table ไม่ใช่ CREATE TABLE แต่อย่างอื่นก็ดี
Stefan Steiger

60

การตั้งค่าแบบไดนามิก

คุณสามารถ "ละเมิด" การตั้งค่าการกำหนดค่าแบบไดนามิกสำหรับสิ่งนี้:

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

การตั้งค่าการกำหนดค่าเป็นค่า varchar เสมอดังนั้นคุณต้องแปลงค่าให้เป็นประเภทข้อมูลที่ถูกต้องเมื่อใช้งาน สิ่งนี้ทำงานได้กับไคลเอนต์ SQL ใด ๆ ในขณะที่\setใช้งานได้เท่านั้นpsql

ข้างต้นต้องใช้ Postgres 9.2 หรือใหม่กว่า

สำหรับเวอร์ชันก่อนหน้าตัวแปรจะต้องประกาศpostgresql.confก่อนใช้งานดังนั้นจึง จำกัด การใช้งานค่อนข้าง จำกัด จริง ๆ แล้วตัวแปรไม่สมบูรณ์ แต่ config "class" ซึ่งเป็นคำนำหน้า แต่เมื่อคำนำหน้าถูกกำหนดตัวแปรใด ๆ สามารถนำมาใช้โดยไม่ต้องเปลี่ยนpostgresql.conf


3
@BrijanElwadhi: ใช่นั่นคือธุรกรรม
a_horse_with_no_name

2
ในฐานะที่เป็นหมายเหตุด้าน: คำบางคำสงวนไว้เช่นการเปลี่ยนแปลงset session my.vars.id = '1';ที่set session my.user.id = '1';จะยอมจำนนERROR: syntax error at or near "user"
dominik

2
@BrijanElwadhi: เพื่อให้การทำธุรกรรมตัวแปรเฉพาะคุณต้องใช้: SET LOCAL .... sessionตัวแปรจะมีผลตราบเท่าที่การเชื่อมต่อที่คุณเป็น localถูกกำหนดขอบเขตในการทำธุรกรรม
Eugen Konkov

@dominik คุณสามารถแก้ไขข้อ จำกัด ดังกล่าวด้วยคำพูดเช่นset session "my.user.id" = '1';การcurrent_setting('my.user.id')โทรทำงานได้ตามที่คาดไว้
Miles Elam

58

ขึ้นอยู่กับลูกค้าของคุณ

อย่างไรก็ตามหากคุณใช้ไคลเอ็นต์psqlคุณสามารถใช้สิ่งต่อไปนี้:

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

หากคุณกำลังใช้ตัวแปรข้อความคุณจะต้องพูด

\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';

1
\setต้องเป็นตัวพิมพ์เล็ก
deluan

db = # \ set profile_id 102 db = #: profile_id; ข้อผิดพลาด: ข้อผิดพลาดทางไวยากรณ์ที่หรือใกล้กับ "102" LINE 1: 102; ^
AlxVallejo

1
@AlxVallejo คุณต้องใช้มันในคำสั่งและคอนโซล psql db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis

21

การใช้ตารางชั่วคราวด้านนอกของ pl / PgSQL

นอกเหนือจากการใช้ pl / pgsql หรือภาษา pl / * อื่น ๆ ตามที่แนะนำนี่เป็นความเป็นไปได้อื่น ๆ เท่านั้นที่ฉันคิดได้

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

13

ฉันต้องการเสนอการปรับปรุงคำตอบของ @ DarioBarrionuevoเพื่อให้ใช้ประโยชน์จากตารางชั่วคราวได้ง่ายขึ้น

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;

ทางออกที่ดีสำหรับการแก้ DO block ไม่สามารถส่งคืนชุดข้อมูลได้!
CodeFarmer

เกี่ยวกับ PostgreSQL 11.0 ผลตอบแทนแบบสอบถามดังกล่าว1(สมมุตินับแถว) tmp_tableมากกว่าเนื้อหาของ
Ed Noepel

9

วิธีการแก้ปัญหานี้ขึ้นอยู่กับfei0x ที่เสนอแต่มีข้อดีที่ไม่จำเป็นต้องเข้าร่วมรายการค่าคงที่ในแบบสอบถามและค่าคงที่สามารถแสดงรายการได้อย่างง่ายดายที่จุดเริ่มต้นของแบบสอบถาม นอกจากนี้ยังทำงานในแบบสอบถามแบบเรียกซ้ำ

โดยทั่วไปค่าคงที่ทุกตัวเป็นตารางค่าเดียวที่ประกาศไว้ในส่วนคำสั่ง WITH ซึ่งสามารถเรียกได้ทุกที่ในส่วนที่เหลือของแบบสอบถาม

  • ตัวอย่างพื้นฐานที่มีค่าคงที่สองค่า:
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)

หรือคุณสามารถใช้SELECT * FROM constant_nameแทนTABLE constant_nameซึ่งอาจไม่ถูกต้องสำหรับภาษาคิวรีอื่น ๆ ที่แตกต่างจาก postgresql


6

นี่คือตัวอย่างการใช้จัดทำงบ คุณยังไม่สามารถใช้งานได้?แต่คุณสามารถใช้$nสัญลักษณ์:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;

ทำงานได้ค่อนข้างดี! ขอบคุณ
Rui Carvalho

4

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

with myVar as (select "any value really")

จากนั้นเมื่อต้องการเข้าถึงค่าที่เก็บไว้ในโครงสร้างนี้คุณต้องดำเนินการ

(select * from myVar)

ตัวอย่างเช่น

with var as (select 123)    
... where id = (select * from var)

3

คุณอาจหันไปใช้คุณสมบัติพิเศษของเครื่องมือ ชอบไวยากรณ์ที่เป็นกรรมสิทธิ์ของ DBeaver:

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

นี่ใกล้เคียงกับการใช้งาน: ฉันจะดูว่า DBeaver สนับสนุนรายการและวนซ้ำ: ฉันต้องใช้ sql เดียวกันกับ schema หลายรายการและรายการจะเป็น schema ที่จะใช้พวกเขา
javadba

1

ใน DBeaver คุณสามารถใช้พารามิเตอร์ในการสืบค้นเช่นเดียวกับที่คุณทำได้จากรหัสดังนั้นสิ่งนี้จะทำงาน:

SELECT *
FROM somewhere
WHERE something = :myvar

เมื่อคุณเรียกใช้แบบสอบถาม DBeaver จะขอค่าจาก: myvar และเรียกใช้แบบสอบถาม

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