【问题标题】:Use variable set by psql meta-command inside of DO block在 DO 块内使用由 psql 元命令设置的变量
【发布时间】:2015-05-13 21:16:36
【问题描述】:

这是我想做的:

\set values foo,bar,baz

DO $$
DECLARE
    value  TEXT;
    values TEXT[] := string_to_array(:'values', ',');
BEGIN
    FOREACH value IN ARRAY values LOOP
        raise notice 'v: %', value;
    END LOOP;
END $$ LANGUAGE plpgsql;

这会导致以下错误:

ERROR:  syntax error at or near ":"
SELECT string_to_array(:'values', ',') INTO values...
                       ^

这是我目前的解决方案,但感觉很hacky:

\set values foo,bar,baz

PREPARE get_values AS SELECT string_to_array(:'values', ',');

DO $$
DECLARE
    value  TEXT;
    values TEXT[];
BEGIN
    EXECUTE 'EXECUTE get_values' INTO values;

    FOREACH value IN ARRAY values LOOP
        raise notice 'v: %', value;
    END LOOP;
END $$ LANGUAGE plpgsql;

【问题讨论】:

    标签: postgresql session-variables dynamic-sql psql postgresql-9.4


    【解决方案1】:

    回答

    DO 需要一个带有 plpgsql 代码的 字符串文字。在 psql 中的字符串中不会替换符号。
    您可以将整个字符串连接到一个 psql 变量中并然后执行它。

    漂亮的多行格式是不可能的,因为 (per documentation):

    但无论如何,元命令的参数不能继续 超出行尾。

    简单示例:

    test=# \set value foo
    test=# \set do 'BEGIN\n   RAISE NOTICE ''v: %'', ' :'value' ';\nEND'
    test=# DO :'do';
    NOTICE:  v: foo
    

    \n 替换换行符(如果您不喜欢漂亮的格式,则将其删除)。基于此改编代码:

    DO
    '
    DECLARE
       _val  text;
       _vals text[] := string_to_array(>>values<<, '','');
    BEGIN
       FOREACH _val IN ARRAY _vals
       LOOP
         RAISE NOTICE ''v: %'', _val;
       END LOOP;
    END
    '
    

    看起来像这样:

    test=# \set do 'DECLARE\n   _val  text;\n   _vals text[] := string_to_array(' :'values' ', '','');\nBEGIN\n   FOREACH _val IN ARRAY _vals\n   LOOP\n     RAISE NOTICE ''v: %'', _val;\n   END LOOP;\nEND'
    test=# DO :'do';
    NOTICE:  v: foo
    NOTICE:  v: bar
    NOTICE:  v: baz
    DO

    我在变量上添加了粗体强调,以便更容易发现。

    @Pavel (ab) 使用 server session variable 的相关回答:

    替代解决方案

    准备好的陈述

    您当前的解决方案看起来还不错。我会简化:

    PREPARE get_values AS SELECT * FROM regexp_split_to_table(:'values', ',');
    
    DO
    $do$
    DECLARE
       _val text;
    BEGIN
       FOR _val IN EXECUTE
          'EXECUTE get_values'
       LOOP
          RAISE NOTICE 'v: %', _val;
       END LOOP;
    END
    $do$;
    

    临时表

    使用临时表的类似解决方案:

    CREATE TEMP TABLE tmp AS SELECT * FROM regexp_split_to_table(:'values', ',') v;
    
    DO
    $do$
    DECLARE
       _val text;
    BEGIN
       FOR _val IN
          TABLE tmp
       LOOP
          RAISE NOTICE 'v: %', _val;
       END LOOP;
    END
    $do$;
    

    【讨论】:

    • 欧文,非常感谢您的回答!你介意看看a solution 我也有评论它的有效性吗?它有效,我只是担心它是否正确/安全。
    【解决方案2】:

    能够利用此解决方案:

    我在哪里设置变量并使用current_setting()检索它

    \set values foo,bar,baz
    SET vars.values TO :'values';
    
    DO $$
    DECLARE
        value  TEXT;
        values TEXT[] := string_to_array(current_setting('vars.values'), ',');
    BEGIN
        FOREACH value IN ARRAY values LOOP
            RAISE NOTICE 'v: %', value;
        END LOOP;
    END $$ LANGUAGE plpgsql
    

    【讨论】:

    • 这也行。就像帕维尔的另一个答案一样。我不会使用像values 这样的reserved word 作为变量名,即使在Postgres 中是允许的。
    • @ErwinBrandstetter 谢谢!它们在代码中的名称不同;我将这篇文章的名称更改为值/值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 2016-03-23
    • 1970-01-01
    • 2021-11-01
    • 2016-04-17
    • 2022-01-27
    • 2016-10-29
    相关资源
    最近更新 更多