你如何在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的最后一句话:
-
如果将它们放在SQL语句中的单引号中,它们不会展开。 因此这不起作用:
SELECT * FROM foo WHERE bar = ':myvariable'
-
要在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
,您也可以从命令行传递psql
variables; 你可以通过-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的一部分吸入。 这真的让我想起了一段时间。 用这种方法评论似乎被视为你所期望的。