你如何在PostgreSQL中使用脚本variables?

在MS SQL Server中,我创build了我的脚本来使用可定制的variables:

DECLARE @somevariable int SELECT @somevariable = -1 INSERT INTO foo VALUES ( @somevariable ) 

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

我如何对PostgreSQL做同样的事情?

谷歌search打开了PSQLvariables ,但这意味着它们只能在其他斜线命令中使用 ,而不是在实际的SQL中使用。

编辑:find我自己的答案,他们其实相当复杂。 sorting帖旧 – >新来跟随我的发现。


find我自己的答案进一步向下链接页面:

psqlvariables的另外一个有用的特性是你可以将它们replace(“插入”)到常规的SQL语句中。

我已经尝试过了,并且遇到了问题,但是这表明我的问题毕竟与variables无关。

Postgresvariables是通过\ set命令创build的,例如…

 \set myvariable value 

…然后可以被replace,例如,…

 SELECT * FROM :myvariable.table1; 

… 要么 …

 SELECT * FROM table1 WHERE :myvariable IS NULL; 

…但是,如果你想使用该variables作为条件string查询中的值,如…

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

…那么你需要包括在variables本身的引号,因为上述不会工作。 相反,定义你的variables…

 \set myvariable 'value' 

但是,如果像我一样,遇到了想从现有variables中创build一个string的情况,我发现这个技巧是…

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

现在你有一个相同的string的引用和不引用的variables! 你可以做这样的事情….

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

关于PSQLvariables的最后一句话:

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

     SELECT * FROM foo WHERE bar = ':myvariable' 
  2. 要在SQL语句中扩展为string文本,您必须将引号包含在variables集中。 但是,variables值已经被括在引号中,这意味着你需要第二组引号,并且内部集合必须被转义。 因此你需要:

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

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

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

特别是对于psql ,您也可以从命令行传递psqlvariables; 你可以通过-v来传递它们。 这是一个用法示例:

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

请注意,冒号未加引号,则自引用的variables名称。 奇怪的语法,我知道。 这只适用于psql; 它不会在(比如说)PgAdmin-III中工作。

这个replace在psql中的input处理过程中发生,所以你不能(定义)使用:'filepath'的函数,并期望:'filepath'的值在会话间切换。 当函数被定义的时候它会被replace一次,然后在那之后将是一个常量。 这对于脚本而言非常有用,但不适用于运行时。

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

您需要使用诸如PL / pgSQL之类的过程语言,而不是SQL过程语言。 在PL / pgSQL中,您可以在SQL语句中正确使用variables。 对于单引号,您可以使用引号文字function。

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

\ set owner_password'thepassword';

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

\ echo:owner_password thepassword;

所以当我尝试使用它时:

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

…我懂了:

创buildangular色myrolelogin未authentication密码thepassword; NOINHERIT CREATEDB CREATEROLE VALID UNTIL'infinity';

这不仅没有在文字上设置引号,而是将命令分为两部分(其中第二部分以“NOINHERIT”开头,是无效的)。

这个故事的寓意:PostgreSQL的“variables”实际上是在文本扩展中使用的macros,而不是真正的值。 我敢肯定,这一点很方便,但起初很棘手。

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

因为一切都在一个string内部被replace的stringvariables需要被转义和引用两次。 使用美元引用,而不会给予SQL注入的全面保护。

另一种方法是(ab)使用PostgreSQL的GUC机制来创buildvariables。 有关详情和示例,请参阅此前的答案 。

你在postgresql.conf声明GUC,然后用SET命令在运行时改变它的值,并用current_setting(...)得到它的值。

我不build议将其用于一般用途,但对于像链接问题中提到的那种狭义情况,它可能是有用的,在这种情况下,发布者希望提供应用程序级别用户名到触发器和函数的方式。

我真的很想念那个function 只有实现类似的方法是使用函数。

我用了两种方法:

  • perl函数使用$ _SHAREDvariables
  • 将你的variables存储在表中

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
  • 这个表版本也不好,因为你必须清除这个偶尔的(而不是删除当前的工作会话variables)

我用临时表解决了它。

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

这样,我有一个“variables”,我可以使用多个查询,这是唯一的会议。 如果导入具有相同用户名的用户,我需要它生成唯一的“用户名”,同时仍然不会发生冲突。

我发现这个问题和答案非常有用,但也令人困惑。 让引用的variables工作,我有很多麻烦,所以这里是我得到它的工作方式:

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

这样你可以在一个语句中定义variables。 当你使用它时,单引号将被embedded到variables中。

注意! 当我在引用的variables后面加注释时,当我尝试了其他答案中的某些方法时,它被作为variables的一部分吸入。 这真的让我想起了一段时间。 用这种方法评论似乎被视为你所期望的。