在 MS SQL Server 中,我创建脚本以使用可自定义的变量:
DECLARE @somevariable int
SELECT @somevariable = -1
INSERT INTO foo VALUES ( @somevariable )
然后,我将在运行时更改 的值@somevariable
,具体取决于我在特定情况下想要的值。由于它位于脚本的顶部,因此很容易查看和记住。
我如何对 PostgreSQL 客户端做同样的事情psql
?
在 MS SQL Server 中,我创建脚本以使用可自定义的变量:
DECLARE @somevariable int
SELECT @somevariable = -1
INSERT INTO foo VALUES ( @somevariable )
然后,我将在运行时更改 的值@somevariable
,具体取决于我在特定情况下想要的值。由于它位于脚本的顶部,因此很容易查看和记住。
我如何对 PostgreSQL 客户端做同样的事情psql
?
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;
关于 PSQL 变量的最后一句话:
如果您在 SQL 语句中将它们括在单引号中,它们不会扩展。因此这不起作用:
SELECT * FROM foo WHERE bar = ':myvariable'
要在 SQL 语句中扩展为字符串文字,您必须在变量集中包含引号。但是,变量值已经必须用引号括起来,这意味着您需要第二组引号,并且必须对内部集进行转义。因此你需要:
\set myvariable '\'somestring\''
SELECT * FROM foo WHERE bar = :myvariable
编辑:从 PostgreSQL 9.1 开始,您可以改为:
\set myvariable somestring
SELECT * FROM foo WHERE bar = :'myvariable'
您可以尝试使用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;
特别是,您也可以从命令行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'
会话之间发生变化。它会在定义函数时被替换一次,然后将是一个常量。它对脚本很有用,但对运行时使用没有用。
FWIW,真正的问题是我在 \set 命令的末尾包含了一个分号:
\set owner_password '密码';
分号被解释为变量中的实际字符:
\echo :owner_password 密码;
所以当我尝试使用它时:
创建角色 myrole 登录未加密密码:owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';
...我懂了:
创建角色 myrole 登录未加密密码 thepassword;NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';
这不仅没有在文字周围设置引号,而且将命令分成两部分(其中第二部分无效,因为它以“NOINHERIT”开头)。
这个故事的寓意:PostgreSQL“变量”实际上是用于文本扩展的宏,而不是真正的值。我敢肯定这会派上用场,但一开始很棘手。
您需要使用其中一种过程语言,例如 PL/pgSQL,而不是 SQL proc 语言。在 PL/pgSQL 中,您可以在 SQL 语句中直接使用 vars。对于单引号,您可以使用引号文字功能。
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 注入的全面保护。
另一种方法是(ab)使用 PostgreSQL GUC 机制来创建变量。有关详细信息和示例,请参阅此先前的答案。
您在 中声明 GUC ,然后在运行时使用命令postgresql.conf
更改其值并使用.SET
current_setting(...)
我不建议将其用于一般用途,但它在链接问题中提到的狭窄情况下可能很有用,在这种情况下,发帖人想要一种方法来为触发器和函数提供应用程序级用户名。
我用临时表解决了它。
CREATE TEMP TABLE temp_session_variables (
"sessionSalt" TEXT
);
INSERT INTO temp_session_variables ("sessionSalt") VALUES (current_timestamp || RANDOM()::TEXT);
这样,我就有了一个可以在多个查询中使用的“变量”,这对于会话来说是唯一的。如果导入具有相同用户名的用户,我需要它来生成唯一的“用户名”,同时仍然不会发生冲突。
我发现这个问题和答案非常有用,但也令人困惑。我在让引用的变量工作时遇到了很多麻烦,所以这是我让它工作的方式:
\set deployment_user username -- username
\set deployment_pass '\'string_password\''
ALTER USER :deployment_user WITH PASSWORD :deployment_pass;
这样,您可以在一个语句中定义变量。当您使用它时,单引号将嵌入到变量中。
笔记!当我在引用的变量之后发表评论时,当我尝试其他答案中的一些方法时,它作为变量的一部分被吸入。这真的让我有一段时间搞砸了。使用这种方法,评论似乎会像您期望的那样被处理。
我真的很怀念那个功能。实现类似功能的唯一方法是使用函数。
我以两种方式使用它:
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';
笔记:
变量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)
是吗?不是整数,是字符串 999;
!
thedatabase=# select 999;
?column?
----------
999
(1 row)
我已经在另一个线程上发布了一个新的解决方案。
It uses a table to store variables, and can be updated at any time. A static immutable getter function is dynamically created (by another function), triggered by update to your table. You get nice table storage, plus the blazing fast speeds of an immutable getter.