如何在psql中使用脚本变量?


133

在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

Answers:


180

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;

66
\set仅用于psql工具,不能在存储过程中使用它!
sorin 2012年

6
@SorinSbarnea OP询问脚本,而不是过程
Daniel Serodio

35
此答案以混乱的方式将psql元命令\set与PostgreSQL命令混合在一起。
Erwin Brandstetter

20
从PostgreSQL 9.1开始,在psql中,您现在可以使用:'variable'将其正确地引用为您的值,或使用:“ variable”将其用作标识符。
HitScan 2013年

9
\set myvariable 'value'包括变量中的任何报价,相反的是这个答案说。如有疑问,请\echo :myvariable在psql中使用以独立于任何查询显示该值。
DanielVérité2014年

61

关于PSQL变量的最后一句话:

  1. 如果将它们用SQL语句括在单引号中,则它们不会扩展。因此,这不起作用:

    SELECT * FROM foo WHERE bar = ':myvariable'
  2. 要在SQL语句中扩展为字符串文字,必须在变量集中包含引号。但是,变量值已经必须用引号引起来,这意味着您需要第二组引号,并且必须对内部引号进行转义。因此,您需要:

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

    编辑:从PostgreSQL 9.1开始,您可以改写:

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

12
干杯:'myvariable'
Andomar '18

正是我想要的!
KeniSteward

47

您可以尝试使用WITH子句。

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;

当您在查询中多次使用相同的计算值时,这种方式最方便。
skaurus 2013年

2
与布莱斯的报告相反,这对我来说似乎很好。 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; 预期杰克(Jack and Jack)的年龄。
2014年

2
对于很多用途,尤其是在Web应用程序框架(如Python Flask)的上下文中,这是在单个查询中重用复杂的计算值的最佳解决方案。
2015年

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':“请注意,冒号未加引号,然后使用变量自身的变量名加引号。” 谢谢!您!我已经在我的书桌上放了一堆额头形的凹痕,以使这项工作有效,而您又为我节省了很多。正是我需要一些脚本。
杰森

13

FWIW,真正的问题是我在\ set命令的末尾添加了分号:

\ set owner_password'密码';

分号被解释为变量中的实际字符:

\ echo:owner_password密码;

因此,当我尝试使用它时:

创建角色myrole登录未加密的密码:owner_password NOINHERIT CREATEDB CREATEROLE VALID直到'infinity';

...我懂了:

创建角色myrole登录未加密的密码。直到“无穷大”为止,NOINHERIT CREATEDB CREATEROLE VALID无效;

这不仅无法在文字周围设置引号,而且将命令分为两部分(第二部分无效,因为它以“ NOINHERIT”开头)。

这个故事的寓意:PostgreSQL“变量”实际上是文本扩展中使用的宏,而不是真正的值。我敢肯定这会派上用场,但一开始它很棘手。


12

您需要使用一种过程语言,例如PL / pgSQL而不是SQL proc语言。在PL / pgSQL中,可以在SQL语句中直接使用vars。对于单引号,可以使用引号文字函数。


5
它不能在postgres本身中完成,但是可以在PSQL客户端应用程序中完成。
Philluminati 2011年

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

由于所有内容都在字符串内部,因此需要对转义的外部字符串变量进行转义并引用两次。取而代之的是,使用美元引号不能完全防止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 '02

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

\ set不是SQL,它是psql内置命令,不支持sql注释。
杰森

2

我真的很想念那个功能。实现类似目的的唯一方法是使用函数。

我以两种方式使用它:

  • 使用$ _SHARED变量的perl函数
  • 将变量存储在表中

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与pg_stat_activity中的backend_start结合使用
  • 该表的版本也很糟糕,因为您必须偶尔清除它(而不要删除当前正在工作的会话变量)

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函数(由另一个函数)。您将获得很好的表存储空间,以及不可变的吸气剂的超快速度。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.