Cách khai báo một biến trong truy vấn PostgreSQL


240

Làm cách nào để khai báo một biến để sử dụng trong truy vấn PostgreQuery 8.3?

Trong MS SQL Server tôi có thể làm điều này:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

Làm thế nào để tôi làm điều tương tự trong PostgreSQL? Theo các biến tài liệu được khai báo đơn giản là "loại tên;", nhưng điều này mang lại cho tôi một lỗi cú pháp:

myvar INTEGER;

Ai đó có thể cho tôi một ví dụ về cú pháp chính xác?


2
Nó có thể được thực hiện chỉ trong PostgreSQL. Xem câu trả lời cho câu hỏi liên quan này: stackoverflow.com/questions/766657/ Khăn
Sean the Bean

2
Câu trả lời liên quan này có câu trả lời tốt hơn: stackoverflow.com/questions/13316773/ Kẻ
Erwin Brandstetter

Câu trả lời:


113

Không có tính năng như vậy trong PostgreSQL. Bạn chỉ có thể làm điều đó trong pl / PGQuery (hoặc pl / *) khác, nhưng không phải bằng SQL đơn giản.

Một ngoại lệ là WITH ()truy vấn có thể hoạt động như một biến hoặc thậm chí tuplecủa các biến. Nó cho phép bạn trả về một bảng các giá trị tạm thời.

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

Tôi đã thử phương pháp này của CTE đang được sử dụng như các vria. Nhưng hơn tôi đã nhanh chóng gặp phải một vấn đề trong đó các truy vấn sửa đổi dữ liệu khác nhau trong CTE không được đảm bảo để thấy hiệu ứng của nhau. Tôi đã phải sử dụng nhiều CTE vì tôi cần sử dụng biến đó trong nhiều truy vấn.
Zia Ul Rehman Mughal

227

Tôi đã hoàn thành mục tiêu tương tự bằng cách sử dụng một WITHmệnh đề , nó không ở đâu thanh lịch nhưng có thể làm điều tương tự. Mặc dù với ví dụ này, nó thực sự quá mức cần thiết. Tôi cũng không đặc biệt khuyên bạn điều này.

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

2
Điều này hoạt động tuyệt vời cho hầu hết các trường hợp mà bạn muốn các biến. Tuy nhiên, nếu bạn muốn sử dụng một biến cho LIMIT (không thể chứa biến), thì bạn muốn sử dụng \setnhư được đề xuất trong câu trả lời của Shahriar Aghajani.
cimmanon

1
Điều này lý tưởng khi tôi có một tập lệnh di chuyển nơi tôi muốn nhập một số dữ liệu quan hệ. Rõ ràng tôi sẽ không biết id chuỗi dữ liệu quan hệ được đưa ra.
Tương đối 13/2/2015

3
Tôi vừa thử phương pháp này, và tìm thấy một cách có lẽ tốt hơn: JOIN myconstants ON truevà sau đó không cần phải chọn phụ.
vektor

7
Điều này chỉ hoạt động trong một truy vấn duy nhất, bạn không thể chia sẻ WITHCTE qua các truy vấn trong một giao dịch.
Daenyth

2
Câu hỏi cũ, nhưng đây là một biến thể : WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. CROSS THAM GIA, là một biểu thức có một hàng đơn, hầu như sao chép dữ liệu cho tất cả các hàng trong bảng thực và đơn giản hóa biểu thức.
Manngo

82

Bạn cũng có thể thử điều này trong 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;

Ở trên yêu cầu Postgres 9.0 trở lên.


1
Câu lệnh DO đã được thêm vào trong PostgreSQL 9.0 và không hoạt động trong 8.3.
Johny

14
Sử dụng TẠO BẢNG TẠM THỜI hoặc TẠO BẢNG TẠM THỜI, không TẠO BẢNG. Nhưng nếu không thì tốt.
Stefan Steiger

60

Cài đặt cấu hình động

bạn có thể "lạm dụng" cài đặt cấu hình động cho việc này:

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

Cài đặt cấu hình luôn là các giá trị varchar, vì vậy bạn cần chuyển chúng sang loại dữ liệu chính xác khi sử dụng chúng. Điều này hoạt động với bất kỳ máy khách SQL nào trong khi \setchỉ hoạt động trongpsql

Ở trên yêu cầu Postgres 9.2 trở lên.

Đối với các phiên bản trước, biến phải được khai báo postgresql.conftrước khi được sử dụng, do đó, nó đã hạn chế phần nào khả năng sử dụng của nó. Trên thực tế không phải là biến hoàn toàn, mà là "lớp" cấu hình về cơ bản là tiền tố. Nhưng một khi tiền tố được xác định, bất kỳ biến nào cũng có thể được sử dụng mà không thay đổipostgresql.conf


3
@BrijanElwadhi: vâng đó là giao dịch.
a_horse_with_no_name

2
Như một lưu ý phụ: một số từ được bảo lưu, ví dụ thay đổi set session my.vars.id = '1';thành set session my.user.id = '1';sẽ mang lạiERROR: syntax error at or near "user"
dominik

2
@BrijanElwadhi: Để thực hiện giao dịch cụ thể, bạn phải sử dụng : SET LOCAL .... Các sessionbiến sẽ có hiệu lực chừng nào bạn kết nối được. Các localphạm vi để giao dịch.
Eugen Konkov

@dominik Bạn có thể vượt qua giới hạn đó bằng dấu ngoặc kép, ví dụ: set session "my.user.id" = '1';Cuộc current_setting('my.user.id')gọi hoạt động như mong đợi.
Miles Elam

58

Nó phụ thuộc vào khách hàng của bạn.

Tuy nhiên, nếu bạn đang sử dụng máy khách psql , thì bạn có thể sử dụng như sau:

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

Nếu bạn đang sử dụng các biến văn bản, bạn cần trích dẫn.

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

1
\setphải viết thường
deluan

db = # \ đặt profile_id 102 db = #: profile_id; LRI: lỗi cú pháp tại hoặc gần "102" LINE 1: 102; ^
AlxVallejo

1
@AlxVallejo bạn phải sử dụng nó trong bảng điều khiển statement và psql . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis

21

Sử dụng Bảng tạm thời bên ngoài pl / PGQuery

Ngoài việc sử dụng pl / pssql hoặc ngôn ngữ pl / * khác như được đề xuất, đây là khả năng khác duy nhất tôi có thể nghĩ đến.

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

13

Tôi muốn đề xuất một cải tiến cho câu trả lời của @ DarioBarrionuevo , để làm cho nó đơn giản hơn khi tận dụng các bảng tạm thời.

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;

giải pháp tốt để giải quyết khối DO không thể trả về tập dữ liệu!
CodeFarmer

Trên PostgreSQL 11.0, một truy vấn như vậy trả về 1(có lẽ là số hàng) chứ không phải là nội dung của tmp_table.
Ed Noepel

9

Giải pháp này dựa trên giải pháp được đề xuất bởi fei0x nhưng nó có ưu điểm là không cần phải tham gia danh sách giá trị của các hằng số trong truy vấn và các hằng số có thể dễ dàng được liệt kê khi bắt đầu truy vấn. Nó cũng hoạt động trong các truy vấn đệ quy.

Về cơ bản, mỗi hằng số là một bảng giá trị đơn được khai báo trong mệnh đề CÓ, sau đó có thể được gọi bất cứ nơi nào trong phần còn lại của truy vấn.

  • Ví dụ cơ bản với hai hằng số:
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)

Ngoài ra, bạn có thể sử dụng SELECT * FROM constant_namethay vì TABLE constant_namecó thể không hợp lệ cho các ngôn ngữ truy vấn khác khác với postgresql.


6

Dưới đây là một ví dụ sử dụng các câu lệnh PREPARE . Bạn vẫn không thể sử dụng ?, nhưng bạn có thể sử dụng $nký hiệu:

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

Hoạt động khá tốt! Cảm ơn.
Rui Carvalho

4

Đúng, không có cách nào sinh động và rõ ràng để khai báo một biến giá trị đơn, điều bạn có thể làm là

with myVar as (select "any value really")

sau đó, để có quyền truy cập vào giá trị được lưu trữ trong công trình này, bạn làm

(select * from myVar)

ví dụ

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

3

Bạn có thể dùng đến các tính năng đặc biệt của công cụ. Giống như cú pháp độc quyền của 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);

Điều này gần với khả năng sử dụng hơn: tôi sẽ xem xét liệu DBeaver có hỗ trợ các danh sách và vòng lặp hay không: tôi cần áp dụng cùng một sql cho nhiều lược đồ và danh sách sẽ là các lược đồ để áp dụng chúng.
javadba

1

Trong DBeaver, bạn có thể sử dụng các tham số trong các truy vấn giống như bạn có thể từ mã, vì vậy điều này sẽ hoạt động:

SELECT *
FROM somewhere
WHERE something = :myvar

Khi bạn chạy truy vấn DBeaver sẽ hỏi bạn giá trị cho: myvar và chạy truy vấn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.