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

xkftehaa  于 2021-06-20  发布在  Mysql
关注(0)|答案(13)|浏览(479)

在ms sql server中,我创建脚本以使用可自定义变量:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

然后我将更改 @somevariable 在运行时,取决于我在特定情况下想要的值。因为它在脚本的顶端,所以很容易看到和记住。
如何对postgresql客户端执行相同的操作 psql ?

smdnsysy

smdnsysy1#

专门为 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' 从一节课改到另一节课。当函数被定义时,它将被替换一次,然后是一个常量。它对于脚本编写很有用,但对于运行时使用却不太有用。

mlmc2os5

mlmc2os52#

另一种方法是(ab)使用postgresql guc机制来创建变量。有关详细信息和示例,请参阅前面的回答。
你宣布guc在 postgresql.conf ,然后在运行时使用 SET 命令并获取其值 current_setting(...) .
我不建议在一般情况下使用它,但它可能在一些狭窄的情况下很有用,比如在链接问题中提到的情况,发帖者想要一种方法来为触发器和函数提供应用程序级别的用户名。

wmtdaxz3

wmtdaxz33#

postgres变量是通过\set命令创建的,例如。。。

\set myvariable value

... 然后可以被替代,例如。。。

SELECT * FROM :myvariable.table1;

... 或者。。。

SELECT * FROM table1 WHERE :myvariable IS NULL;

编辑:从PSQL9.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;
ejk8hzay

ejk8hzay4#

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注入。

wr98u20j

wr98u20j5#

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

uqjltbpv6#

我用临时表解决了。

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

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

9nvpjoqh

9nvpjoqh7#

中的变量 psql 糟透了。如果要声明整数,必须输入整数,然后执行回车,然后以分号结束语句。观察:
假设我要声明一个整数变量 my_var 把它放进一张table里 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)
eiee3dmh

eiee3dmh8#

我发现这个问题和答案非常有用,但也令人困惑。我很难让引用的变量工作,所以下面是我让它工作的方法:

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

这样就可以在一条语句中定义变量。使用它时,单引号将嵌入变量中。
注意!当我在引用的变量后面添加注解时,当我在其他答案中尝试某些方法时,它被作为变量的一部分吸入。那真的把我搞砸了。使用此方法时,注解似乎会像您期望的那样被处理。

x6492ojm

x6492ojm9#

fwiw,真正的问题是我在\set命令的末尾加了一个分号:
\设置所有者密码'thepassword';
分号被解释为变量中的实际字符:
\echo:owner\u password密码;
所以当我尝试使用它时:
创建角色myrole登录未加密密码:owner_password noinherit createdb createrole有效期至'infinity';
…我知道了:
创建角色myrole登录未加密密码password;noinherit createdb createrole在“无限”之前有效;
这不仅无法在文本周围设置引号,而且还将命令分为两部分(第二部分是无效的,因为它以“noinherit”开头)。
这个故事的寓意是:postgresql“变量”实际上是用于文本扩展的宏,而不是真正的值。我相信这会派上用场,但一开始很棘手。

goqiplq2

goqiplq210#

你可以试着用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;
weylhg0b

weylhg0b11#

您需要使用过程语言之一,如pl/pgsql,而不是sqlproc语言。在pl/pgsql中,可以在sql语句中使用vars。对于单引号,可以使用quote literal函数。

zc0qhyus

zc0qhyus12#

我真的很怀念那个功能。实现类似功能的唯一方法就是使用函数。
我用了两种方法:
使用$\u共享变量的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\u backend\u pid不是最好的会话标识,请考虑使用pid结合backend\u从pg\u stat\u活动开始
此表版本也不好,因为您必须偶尔清除它(而不是删除当前正在工作的会话变量)

q7solyqu

q7solyqu13#

我在另一个线程上发布了一个新的解决方案。
它使用一个表来存储变量,并且可以随时更新。静态不可变getter函数是动态创建的(由另一个函数创建),由表的更新触发。你可以得到很好的表存储,加上不可变getter的极快速度。

相关问题