Làm thế nào để bạn sử dụng các biến script trong psql?


133

Trong MS SQL Server, tôi tạo các tập lệnh của mình để sử dụng các biến tùy chỉnh:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

Sau đó, tôi sẽ thay đổi giá trị của @somevariablethời gian chạy, tùy thuộc vào giá trị mà tôi muốn trong tình huống cụ thể. Vì nó ở đầu kịch bản nên dễ nhìn và dễ nhớ.

Làm cách nào để thực hiện tương tự với ứng dụng khách PostgreSQL psql?


5
FWIW, toán tử \ set dường như có liên quan đến công cụ dòng lệnh psql, không liên quan đến ngôn ngữ bó pssql. Tôi có thể sai.
Daniel Yankowsky

1
Phiên bản nào của Postgres?
Kuberchaun

Câu trả lời:


180

Các biến Postgres được tạo thông qua lệnh \ set, ví dụ ...

\set myvariable value

... và sau đó có thể được thay thế, ví dụ như ...

SELECT * FROM :myvariable.table1;

... hoặc là ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

chỉnh sửa: Kể từ psql 9.1, các biến có thể được mở rộng trong dấu ngoặc kép như trong:

\set myvariable value 

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

Trong các phiên bản cũ hơn của máy khách psql:

... Nếu bạn muốn sử dụng biến làm giá trị trong truy vấn chuỗi có điều kiện, chẳng hạn như ...

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

... Sau đó, bạn cần bao gồm các trích dẫn trong chính biến vì ở trên sẽ không hoạt động. Thay vào đó, hãy xác định biến của bạn như vậy ...

\set myvariable 'value'

Tuy nhiên, nếu như tôi, bạn gặp phải một tình huống trong đó bạn muốn tạo một chuỗi từ một biến hiện có, tôi đã tìm thấy mẹo để ...

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

Bây giờ bạn có cả một biến được trích dẫn và không trích dẫn của cùng một chuỗi! Và bạn có thể làm một cái gì đó như thế này ....

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

66
\setchỉ dành cho psqlcông cụ, bạn không thể sử dụng nó trong các thủ tục được lưu trữ!
sorin

6
@SorinSbarnea OP đã hỏi về kịch bản , không phải thủ tục
Daniel Serodio

35
Câu trả lời này trộn psqlcác lệnh meta \setvới các lệnh PostgreSQL một cách khó hiểu.
Erwin Brandstetter

20
Kể từ postgresql 9.1, trong psql bây giờ bạn có thể sử dụng: 'biến' để được trích dẫn chính xác làm giá trị cho bạn hoặc: "biến" để sử dụng nó làm định danh.
HitScan

9
\set myvariable 'value'không không bao gồm bất kỳ quote bên trong biến, trái với những gì câu trả lời này nói. Khi nghi ngờ, hãy sử dụng \echo :myvariabletrong psql để hiển thị giá trị độc lập của bất kỳ truy vấn nào.
Daniel Vérité

61

Một từ cuối cùng về các biến PSQL:

  1. Chúng không mở rộng nếu bạn đặt chúng trong dấu ngoặc đơn trong câu lệnh SQL. Do đó, điều này không hoạt động:

    SELECT * FROM foo WHERE bar = ':myvariable'
  2. Để mở rộng thành một chuỗi ký tự trong câu lệnh SQL, bạn phải bao gồm các dấu ngoặc kép trong tập biến. Tuy nhiên, giá trị biến đã được đặt trong dấu ngoặc kép, điều đó có nghĩa là bạn cần một bộ dấu ngoặc kép thứ hai và bộ bên trong phải được thoát. Vì vậy, bạn cần:

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

    EDIT : bắt đầu với PostgreSQL 9.1, bạn có thể viết thay thế:

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

12
Chúc mừng:'myvariable'
Andomar

Chính xác những gì tôi đang tìm kiếm!
KeniSteward

47

Bạn có thể thử sử dụng mệnh đề .

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;

Cách này chủ yếu là thuận tiện khi bạn đang sử dụng cùng một giá trị được tính toán vài lần trong truy vấn của mình.
skaurus

2
Trái với báo cáo của Bryce, nó dường như hoạt động tốt với tôi. 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; Tuổi của Ouputs Jack và Jack, như mong đợi.
Joshua

2
Đối với nhiều mục đích sử dụng, đặc biệt là trong bối cảnh của khung ứng dụng web như Python Flask, đây là giải pháp tốt nhất để sử dụng lại các giá trị được tính toán phức tạp trong một truy vấn duy nhất.
Sẽ

1
Bất cứ ai có thể đề nghị làm thế nào điều này có thể làm việc trong một chèn?
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

Cụ thể cho psql , bạn cũng có thể truyền psqlcác biến từ dòng lệnh; bạn có thể vượt qua chúng với -v. Đây là một ví dụ sử dụng:

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

Lưu ý rằng dấu hai chấm không được trích dẫn, sau đó tên biến của chính nó được trích dẫn. Cú pháp lạ, tôi biết. Điều này chỉ hoạt động trong psql; nó sẽ không hoạt động trong (giả sử) PGAdmin-III.

Sự thay thế này xảy ra trong quá trình xử lý đầu vào trong psql, vì vậy bạn không thể (nói) xác định hàm sử dụng :'filepath'và mong đợi giá trị :'filepath'thay đổi từ phiên này sang phiên khác. Nó sẽ được thay thế một lần, khi chức năng được xác định, và sau đó sẽ là một hằng số. Nó hữu ích cho kịch bản nhưng không sử dụng thời gian chạy.


Các biến psql, ví dụ: 'filepath', bạn đã chỉ ra: "Lưu ý rằng dấu hai chấm không được trích dẫn, sau đó tên biến tự nó được trích dẫn." Cảm tạ! Bạn! Tôi đã đặt một loạt các vết lõm hình trán vào bàn của mình để cố gắng thực hiện công việc này, và bạn chỉ tiết kiệm cho tôi thêm một tấn. Chính xác những gì tôi cần cho một số kịch bản.
Jason

13

FWIW, vấn đề thực sự là tôi đã bao gồm một dấu chấm phẩy ở cuối lệnh \ set:

\ đặt chủ sở hữu_password 'mật khẩu';

Dấu chấm phẩy được hiểu là một ký tự thực tế trong biến:

\ echo: own_password thepassword;

Vì vậy, khi tôi cố gắng sử dụng nó:

TẠO VAI TRÒ myrole ĐĂNG NHẬP PASSWORD: own_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'vô cùng';

...Tôi hiểu rồi:

TẠO VAI TRÒ myrole ĐĂNG NHẬP PASSWORD mật khẩu; NOINHERIT CREATEDB CREATEROLE GIÁ TRỊ UNTIL 'vô cùng';

Điều đó không chỉ thất bại trong việc đặt các trích dẫn xung quanh nghĩa đen mà còn chia lệnh thành 2 phần (phần thứ hai không hợp lệ khi bắt đầu bằng "NOINHERIT").

Đạo đức của câu chuyện này: "biến" PostgreSQL thực sự là các macro được sử dụng trong việc mở rộng văn bản, không phải là giá trị thực. Tôi chắc chắn rằng nó có ích, nhưng ban đầu nó khó khăn.


12

Bạn cần sử dụng một trong những ngôn ngữ thủ tục, chẳng hạn như PL / pgSQL chứ không phải ngôn ngữ Proc của SQL. Trong PL / pgSQL, bạn có thể sử dụng vars ngay trong các câu lệnh SQL. Đối với dấu ngoặc đơn bạn có thể sử dụng chức năng trích dẫn bằng chữ.


5
Nó không thể được thực hiện trong chính postgres, nhưng nó có thể được thực hiện trong ứng dụng khách PSQL.
Philluminati

1
plpgsql có thể (bây giờ) được sử dụng trong postgres (kể từ phiên bản 9.0)) postgresql.org/docs/9.0/static/sql-do.html
Jasen

11

postgres (kể từ phiên bản 9.0) cho phép các khối ẩn danh trong bất kỳ ngôn ngữ kịch bản phía máy chủ nào được hỗ trợ

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

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

Vì mọi thứ đều nằm trong một chuỗi, các biến chuỗi bên ngoài được thay thế sẽ cần phải được thoát và trích dẫn hai lần. Thay vào đó, sử dụng trích dẫn đô la sẽ không bảo vệ hoàn toàn chống lại việc tiêm SQL.


5

Một cách tiếp cận khác là (ab) sử dụng cơ chế GUC PostgreSQL để tạo các biến. Xem câu trả lời trước để biết chi tiết và ví dụ.

Bạn khai báo GUC postgresql.conf, sau đó thay đổi giá trị của nó khi chạy bằng SETcác lệnh và nhận giá trị của nó vớicurrent_setting(...) .

Tôi không khuyến nghị sử dụng chung cho mục đích chung này, nhưng nó có thể hữu ích trong các trường hợp hẹp như câu hỏi được đề cập trong câu hỏi được liên kết, trong đó người đăng muốn có cách cung cấp tên người dùng cấp ứng dụng cho các trình kích hoạt và chức năng.


4

Tôi đã giải quyết nó bằng một bảng tạm thời.

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

Bằng cách này, tôi đã có một "biến" tôi có thể sử dụng qua nhiều truy vấn, đó là duy nhất cho phiên. Tôi cần nó để tạo "tên người dùng" duy nhất trong khi vẫn không có xung đột nếu nhập người dùng có cùng tên người dùng.


Đây dường như là cách làm việc duy nhất trong các công cụ trực quan như Heidi SQL.
Altair7852

2

Tôi đã tìm thấy câu hỏi này và câu trả lời cực kỳ hữu ích, nhưng cũng khó hiểu. Tôi gặp rất nhiều khó khăn khi các biến được trích dẫn hoạt động, vì vậy đây là cách tôi làm cho nó hoạt động:

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

Bằng cách này bạn có thể định nghĩa biến trong một câu lệnh. Khi bạn sử dụng nó, dấu ngoặc đơn sẽ được nhúng vào biến.

GHI CHÚ! Khi tôi đặt một nhận xét sau biến được trích dẫn, nó đã bị hút vào như một phần của biến khi tôi thử một số phương thức trong các câu trả lời khác. Điều đó thực sự làm tôi khó chịu trong một thời gian. Với phương pháp này, các bình luận dường như được xử lý như bạn mong đợi.


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

\ set không phải là SQL, đó là một nhận xét sql lệnh tích hợp sẵn không được hỗ trợ.
Jasen

2

Tôi thực sự nhớ tính năng đó. Cách duy nhất để đạt được một cái gì đó tương tự là sử dụng các chức năng.

Tôi đã sử dụng nó theo hai cách:

  • các hàm perl sử dụng biến $ _SHARED
  • lưu trữ các biến của bạn trong bảng

Phiên bản 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;

Phiên bản bảng:

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

Ghi chú:

  • plperlu nhanh hơn perl
  • pg_backend_pid không phải là nhận dạng phiên tốt nhất, hãy xem xét sử dụng pid kết hợp với backend_start từ pg_stat_activity
  • phiên bản bảng này cũng tệ vì thỉnh thoảng bạn phải xóa cái này (và không xóa các biến phiên hiện đang hoạt động)

1

Biến trong psql mút. Nếu bạn muốn khai báo một số nguyên, bạn phải nhập số nguyên, sau đó thực hiện trả về vận chuyển, sau đó kết thúc câu lệnh bằng dấu chấm phẩy. Quan sát:

Giả sử tôi muốn khai báo một biến số nguyên my_varvà chèn nó vào một bảng test:

Bảng ví dụ 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)

Rõ ràng, không có gì trong bảng này:

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

Chúng tôi khai báo một biến. Lưu ý cách dấu chấm phẩy trên dòng tiếp theo!

thedatabase=# \set my_var 999
thedatabase=# ;

Bây giờ chúng ta có thể chèn. Chúng ta phải sử dụng :''cú pháp tìm kiếm " " kỳ lạ này :

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

Nó đã làm việc!

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

Giải trình:

Vậy ... chuyện gì xảy ra nếu chúng ta không có dấu chấm phẩy trên dòng tiếp theo? Các biến? Có một cái nhìn:

Chúng tôi tuyên bố my_varmà không có dòng mới.

thedatabase=# \set my_var 999;

Hãy chọn my_var.

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

WTF là gì? Nó không phải là số nguyên , nó là một chuỗi 999; !

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

5
Lý do dấu chấm phẩy làm những điều không mong muốn đối với bạn là vì dấu chấm phẩy chấm dứt câu lệnh SQL, nhưng bạn đang gõ một lệnh psql, \ set, không phải là SQL và KHÔNG lấy dấu chấm phẩy. Đặt một dấu chấm phẩy trên dòng tiếp theo sẽ không bị tổn thương, nhưng hoàn toàn không có gì. Đó là một tuyên bố trống rỗng.
volkerk

1

Tôi đã đăng một giải pháp mới cho vấn đề này trên một chủ đề khác .

Nó sử dụng một bảng để lưu trữ các biến và có thể được cập nhật bất cứ lúc nào. Một hàm getter bất biến tĩnh được tạo động (bởi một hàm khác), được kích hoạt bằng cách cập nhật vào bảng của bạn. Bạn có được lưu trữ bảng tốt đẹp, cộng với tốc độ nhanh chóng của một getter bất biế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.