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

Cra*_*ker 122 sql postgresql variables psql

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

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )
Run Code Online (Sandbox Code Playgroud)

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

我如何对PostgreSQL客户端做同样的事情psql

cro*_*umb 171

Postgres变量是通过\ set命令创建的,例如......

\set myvariable value
Run Code Online (Sandbox Code Playgroud)

......然后可以替换,例如......

SELECT * FROM :myvariable.table1;
Run Code Online (Sandbox Code Playgroud)

... 要么 ...

SELECT * FROM table1 WHERE :myvariable IS NULL;
Run Code Online (Sandbox Code Playgroud)

编辑:从psql 9.1开始,变量可以用引号扩展,如:

\set myvariable value 

SELECT * FROM table1 WHERE column1 = :'myvariable';
Run Code Online (Sandbox Code Playgroud)

在旧版本的psql客户端中:

...如果要将变量用作条件字符串查询中的值,例如...

SELECT * FROM table1 WHERE column1 = ':myvariable';
Run Code Online (Sandbox Code Playgroud)

...然后你需要在变量本身中包含引号,因为上面的代码不起作用.而是将变量定义为......

\set myvariable 'value'
Run Code Online (Sandbox Code Playgroud)

但是,如果像我一样,你遇到了你想从现有变量中创建一个字符串的情况,我发现了这个诀窍......

\set quoted_myvariable '\'' :myvariable '\''
Run Code Online (Sandbox Code Playgroud)

现在你有一个相同字符串的带引号和无引号变量!你可以做这样的事....

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
Run Code Online (Sandbox Code Playgroud)

  • `\ set`仅适用于`psql`工具,你不能在存储过程中使用它! (58认同)
  • 这个答案以一种令人困惑的方式将`psql`元命令`\ set`与PostgreSQL命令混合在一起. (31认同)
  • 从postgresql 9.1开始,在psql中,您现在可以使用:'variable'将其正确引用为您的值,或者:"variable"将其用作标识符. (20认同)
  • `\ set myvariable'value'`确实**不包括变量中的任何引号,与此答案所说的相反.如有疑问,请在psql中使用`\ echo:myvariable`来独立显示任何查询的值. (8认同)
  • @SorinSbarnea OP询问*脚本*,而不是*程序* (6认同)
  • `\set myvar 'value'` 无法按照您描述的方式工作。您必须使用 `\set myvar '\'value\''` (2认同)
  • @laurent:或者像@HitScan一样使用`:'myvar' (2认同)

Cra*_*ker 58

关于PSQL变量的最后一句话:

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

    SELECT * FROM foo WHERE bar = ':myvariable'
    
    Run Code Online (Sandbox Code Playgroud)
  2. 要在SQL语句中扩展为字符串文字,必须在变量集中包含引号.但是,变量值必须用引号括起来,这意味着您需要第二组引号,并且必须对内部集进行转义.因此你需要:

    \set myvariable '\'somestring\''  
    SELECT * FROM foo WHERE bar = :myvariable
    
    Run Code Online (Sandbox Code Playgroud)

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

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
    
    Run Code Online (Sandbox Code Playgroud)

  • 为':'myvariable'`干杯 (11认同)

ska*_*rus 42

您可以尝试使用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;
Run Code Online (Sandbox Code Playgroud)

  • 它似乎不适用于`WHERE`子句.公平警告. (3认同)
  • 与布莱斯的报告相反,它似乎对我来说很好.`CREATE TABLE test(名称VARCHAR,年龄INT);``INSERT INTO test(名称,年龄)VALUES('Jack',21),('Jill',20);``WITH vars AS(SELECT N'Jack' AS名称,21 AS年龄)SELECT测试.*FROM测试,vars WHERE test.name = vars.name和test.age = vars.age;`输出杰克和杰克的年龄,如预期的那样. (2认同)
  • 对于很多用途,特别是在像Python Flask这样的Web应用程序框架的上下文中,这是在单个查询中重用复杂计算值的最佳解决方案. (2认同)

Cra*_*ger 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)
Run Code Online (Sandbox Code Playgroud)

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

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


Cra*_*ker 13

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

\ set owner_password'thepassword';

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

\ echo:owner_password thepassword;

所以当我尝试使用它时:

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

...我懂了:

创造角色myrole登录UNENCRYPTED PASSWORD thepassword; NOINHERIT CREATEDB CREATEROLE有效'无限';

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

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


小智 12

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

  • 它不能在postgres中完成,但可以在PSQL客户端应用程序中完成. (5认同)

Jas*_*sen 10

postgres(自9.0版开始)允许使用任何受支持的服务器端脚本语言中的匿名块

DO '
DECLARE somevariable int = -1;
BEGIN
INSERT INTO foo VALUES ( somevariable );
END
' ;
Run Code Online (Sandbox Code Playgroud)

http://www.postgresql.org/docs/current/static/sql-do.html

由于所有内容都在字符串中,因此需要对外部字符串变量进行转义并引用两次.使用美元引用代替不会提供针对SQL注入的完全保护.


Cra*_*ger 5

另一种方法是(ab)使用PostgreSQL GUC机制创建变量。有关详细信息和示例,请参见此先前的答案

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

我不建议将此方法用于一般用途,但是在狭窄的情况下(如链接问题中提到的情况)可能会有用,在这种情况下,发帖者需要一种为触发器和函数提供应用程序级用户名的方法。


Kai*_*aur 5

我真的很怀念这个功能。实现类似功能的唯一方法是使用函数。

我通过两种方式使用它:

  • 使用 $_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;
Run Code Online (Sandbox Code Playgroud)

表版本:

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';
Run Code Online (Sandbox Code Playgroud)

笔记:

  • plperlu 比 perl 更快
  • pg_backend_pid 不是最好的会话标识,请考虑将 pid 与 pg_stat_activity 中的 backend_start 结合使用
  • 这个表版本也很糟糕,因为你必须偶尔清除它(并且不要删除当前工作的会话变量)


geo*_*eon 5

我用临时表解决了它。

CREATE TEMP TABLE temp_session_variables (
    "sessionSalt" TEXT
);
INSERT INTO temp_session_variables ("sessionSalt") VALUES (current_timestamp || RANDOM()::TEXT);
Run Code Online (Sandbox Code Playgroud)

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