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

时间:2008-08-31 16:54:33

标签: sql postgresql variables psql

在MS SQL Server中,我创建脚本以使用可自定义的变量:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

然后我将在运行时更改@somevariable的值,具体取决于我在特定情况下所需的值。由于它位于脚本的顶部,因此很容易看到并记住。

如何对PostgreSQL客户端psql执行相同操作?

13 个答案:

答案 0 :(得分:163)

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;

答案 1 :(得分:57)

关于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'
    

答案 2 :(得分:40)

您可以尝试使用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;

答案 3 :(得分:28)

特别是对于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)

请注意冒号是不加引号的,然后是引用其self的变量名。奇怪的语法,我知道。这仅适用于psql;它不适用于(例如)PgAdmin-III。

此替换发生在psql的输入处理期间,因此您不能(比方说)定义使用:'filepath'并期望:'filepath'的值在会话之间更改的函数。当函数被定义时,它将被替换一次,之后将是一个常量。它对脚本有用,但不适用于运行时使用。

答案 4 :(得分:12)

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

  

\ set owner_password'thepassword';

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

  

\ echo:owner_password   thepassword;

所以当我尝试使用它时:

  

CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD:owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL'infinity';

......我明白了:

  

CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD thepassword; NOINHERIT CREATEDB CREATEROLE有效'无限';

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

这个故事的寓意:PostgreSQL“变量”实际上是用于文本扩展的宏,而不是真正的值。我确信它会派上用场,但起初它很棘手。

答案 5 :(得分:12)

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

答案 6 :(得分:9)

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注入的全面保护。

答案 7 :(得分:5)

另一种方法是(ab)使用PostgreSQL GUC机制来创建变量。有关详细信息和示例,请参阅this prior answer

您在postgresql.conf中声明GUC,然后使用SET命令在运行时更改其值,并使用current_setting(...)获取其值。

我不建议将其用于一般用途,但它可能在链接问题中提到的狭义情况下很有用,其中海报想要一种方法来为触发器和函数提供应用程序级用户名。

答案 8 :(得分:3)

我用临时表解决了它。

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

这样,我有一个“变量”,我可以使用多个查询,这对于会话是唯一的。如果导入具有相同用户名的用户,我需要它来生成唯一的“用户名”,同时仍然没有冲突。

答案 9 :(得分:2)

我发现这个问题和答案非常有用,但也令人困惑。我在引用变量工作方面遇到了很多麻烦,所以这就是我使用它的方式:

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

这样您就可以在一个语句中定义变量。当您使用它时,单引号将嵌入到变量中。

请注意!当我在引用变量之后发表评论时,当我在其他答案中尝试了一些方法时,它作为变量的一部分被吸入。这真让我搞砸了一段时间。使用此方法,评论似乎可以按照您的预期进行处理。

答案 10 :(得分: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结合使用
  • 这个表版本也很糟糕,因为你必须偶尔清除它(而不是删除当前工作的会话变量)

答案 11 :(得分: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)

答案 12 :(得分:1)

我为此on another thread发布了新的解决方案。

它使用表来存储变量,并且可以随时更新。通过更新表触发动态创建静态不可变getter函数(由另一个函数)。您将获得漂亮的表存储空间,以及不可变的吸气剂的超快速度。