关于sql:如何在psql中使用脚本变量?

关于sql:如何在psql中使用脚本变量?

How do you use script variables in psql?

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

1
2
3
4
DECLARE @somevariable INT  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

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

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


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

1
\SET myvariable VALUE

...,然后可以替换为...

1
SELECT * FROM :myvariable.table1;

... 要么 ...

1
SELECT * FROM table1 WHERE :myvariable IS NULL;

编辑:从psql 9.1开始,变量可以用引号引起来,如下所示:

1
2
3
\SET myvariable VALUE

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

在旧版本的psql客户端中:

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

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

...那么您需要在变量本身中包含引号,因为上述内容将无法正常工作。而是这样定义您的变量...

1
\SET myvariable 'value'

但是,如果像我一样,您遇到了要从现有变量中创建字符串的情况,我发现窍门就是...

1
\SET quoted_myvariable '\'' :myvariable '\''

现在,您同时具有相同字符串的带引号和不带引号的变量!而且你可以做这样的事情....

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

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

  • 如果将它们用SQL语句括在单引号中,它们将不会扩展。
    因此,这不起作用:

    1
    SELECT * FROM foo WHERE bar = ':myvariable'
  • 要在SQL语句中扩展为字符串文字,必须在变量集中包括引号。但是,变量值已经必须用引号引起来,这意味着您需要第二组引号,并且必须对内部引号进行转义。因此,您需要:

    1
    2
    \SET myvariable '\'somestring\''  
    SELECT * FROM foo WHERE bar = :myvariable

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

    1
    2
    \SET myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'

  • 您可以尝试使用WITH子句。

    1
    2
    3
    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传递它们。这是一个用法示例:

    1
    2
    3
    4
    5
    6
    $ psql -v filepath=/path/TO/my/directory/mydatafile.data regress
    regress=> SELECT :'filepath';
                   ?COLUMN?                
    ---------------------------------------
     /path/TO/my/directory/mydatafile.data
    (1 ROW)

    注意,冒号未加引号,然后变量名称" self"被加引号。我知道语法很奇怪。这仅在psql中有效; (例如)PgAdmin-III无法使用。

    这种替换发生在psql中的输入处理期间,因此您不能(说)定义一个使用:'filepath'的函数,并且期望:'filepath'的值在会话之间改变。定义函数后,它将被替换一次,之后将是一个常量。这对于脚本编写很有用,但对运行时不起作用。


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

    \set owner_password 'thepassword';

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

    \echo :owner_password
    thepassword;

    因此,当我尝试使用它时:

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

    ...我懂了:

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

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

    这个故事的寓意:PostgreSQL"变量"实际上是文本扩展中使用的宏,而不是真正的值。我敢肯定这会派上用场,但一开始它很棘手。


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


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

    1
    2
    3
    4
    5
    6
    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机制创建变量。有关详细信息和示例,请参见此先前的答案。

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

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


    我用一个临时表解决了它。

    1
    2
    3
    4
    CREATE TEMP TABLE temp_session_variables (
       "sessionSalt" TEXT
    );
    INSERT INTO temp_session_variables ("sessionSalt") VALUES (CURRENT_TIMESTAMP || RANDOM()::TEXT);

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


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

    我以两种方式使用它:

    • 使用$ _SHARED变量的perl函数
    • 将变量存储在表中

    Perl版本:

    1
    2
    3
    4
    5
    6
       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;

    表格版本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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结合使用
    • 该表版本也很糟糕,因为您必须偶尔清除它(而不是删除当前正在工作的会话变量)

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

    1
    2
    3
    \SET deployment_user username    -- username
    \SET deployment_pass '\'string_password\''
    ALTER USER :deployment_user WITH PASSWORD :deployment_pass;

    这样,您可以在一个语句中定义变量。使用它时,单引号将嵌入到变量中。

    注意!当我在带引号的变量后添加注释时,当我尝试其他答案中的某些方法时,它作为变量的一部分被吸收。那真是让我有些困惑。使用这种方法,注释似乎可以按照您的期望进行处理。


    我在另一个线程上发布了一个新的解决方案。

    它使用表来存储变量,并且可以随时更新。通过更新表触发动态创建静态不可变getter函数(由另一个函数)。您将获得漂亮的表存储空间,以及不可变的吸气剂的超快速度。


    psql中的变量很烂。如果要声明整数,则必须输入整数,然后执行回车,然后以分号结束语句。观察:

    假设我要声明一个整数变量my_var并将其插入表test中:

    示例表test

    1
    2
    3
    4
    5
    6
    7
    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)

    显然,此表中没有任何内容:

    1
    2
    3
    4
    thedatabase=# SELECT * FROM test;
     id
    ----
    (0 ROWS)

    我们声明一个变量。注意下一行的分号是如何!

    1
    2
    thedatabase=# \SET my_var 999
    thedatabase=# ;

    现在我们可以插入。我们必须使用这种奇怪的" :''"语法:

    1
    2
    thedatabase=# INSERT INTO test(id) VALUES (:'my_var');
    INSERT 0 1

    有效!

    1
    2
    3
    4
    5
    thedatabase=# SELECT * FROM test;
     id  
    -----
     999
    (1 ROW)

    说明:

    那么...如果下一行没有分号怎么办?变量?看一看:

    我们声明my_var而不换行。

    1
    thedatabase=# \SET my_var 999;

    让我们选择my_var

    1
    2
    3
    4
    5
    thedatabase=# SELECT :'my_var';
     ?COLUMN?
    ----------
     999;
    (1 ROW)

    WTF是吗?它不是整数,而是字符串999;

    1
    2
    3
    4
    5
    thedatabase=# SELECT 999;
     ?COLUMN?
    ----------
          999
    (1 ROW)


    推荐阅读

      linux命令创建项目组?

      linux命令创建项目组?,管理,密码,项目,命令,系统,位置,文件,用户组,用户,文

      linux脚本命令输信息?

      linux脚本命令输信息?,系统,信息,代码,脚本,数据,工具,发行,命令,变量,文件,l

      linux命令简写自定义?

      linux命令简写自定义?,系统,状态,命令,代码,工具,发行,标准,软件,文件,别名,L

      linux的创建目录命令?

      linux的创建目录命令?,名字,地址,位置,密码,软件,系统,命令,目录,文件夹,文

      linux上启动脚本命令?

      linux上启动脚本命令?,服务,状态,系统,代码,脚本,工作,周期性,命令,文件,方

      linux创建端口命令?

      linux创建端口命令?,系统,网络,服务,通讯,检测,工具,端口,电脑,命令,以下,Lin

      linux下文件创建命令?

      linux下文件创建命令?,名字,名称,首次,命令,文件,系统,密码,文件名,文件夹,

      linux创建vp命令?

      linux创建vp命令?,系统,工作,基础,设备,地址,命令,目录,环境,信息,工具,linux

      linux下编写脚本命令?

      linux下编写脚本命令?,代码,时间,工具,标准,系统,实战,平台,最新,网站,文件,l

      linux命令创建文件加?

      linux命令创建文件加?,名字,管理,系统,名称,密码,首次,命令,文件,文件夹,位

      linux用户自定义命令?

      linux用户自定义命令?,系统,时间,标准,软件,项目,电脑,服务,工具,基本知识,

      linux定时器脚本命令?

      linux定时器脚本命令?,系统,代码,时间,工具,工作,定期,周期性,异常,任务,脚

      linux停止脚本命令行?

      linux停止脚本命令行?,系统,工作,平台,命令,基础,第一,环境,信息,终端,程序,

      linux命令运行脚本?

      linux命令运行脚本?,代码,系统,工作,设计,状态,命令,脚本,文件,目录,终端,运

      linux脚本赋权限命令?

      linux脚本赋权限命令?,系统,档案,工作,命令,工具,脚本,权限,文件,程序,文件

      linux变量是一个命令?

      linux变量是一个命令?,系统,信息,变量,名称,官网,地址,环境,代码,地方,命令,$

      linux按命令创建磁盘?

      linux按命令创建磁盘?,系统,信息,业务,号码,数据,情况,电脑,分区,较大,工具,

      linux命令中创建文本?

      linux命令中创建文本?,系统,时间,文件,终端,名字,名称,发行,命令,文件夹,文

      linux常用命令创建?

      linux常用命令创建?,地址,系统,工作,时间,命令,管理,文件,目录,路径,控制台,l

      linux关闭脚本命令?

      linux关闭脚本命令?,系统,服务,状态,命令,档案,暂停,软件,工具,数据,电脑,lin