【问题标题】:Sequence name taken from variable序列名称取自变量
【发布时间】:2014-09-19 11:09:36
【问题描述】:

如何创建一个新序列,其名称来自变量?

我们来看下面的例子:

CREATE OR REPLACE FUNCTION get_value(_name_part character varying)
  RETURNS INTEGER AS
$BODY$
DECLARE
    result bigint;
    sequencename character varying(50);
BEGIN
    sequencename = CONCAT('constant_part_of_name_', _name_part);
    IF((SELECT CAST(COUNT(*) AS INTEGER) FROM pg_class
        WHERE relname LIKE sequencename) = 0)
    THEN
       CREATE SEQUENCE sequencename --here is the guy this is all about
       MINVALUE 6000000
       INCREMENT BY 1;
    END IF;
    SELECT nextval(sequencename) INTO result;
    RETURN result;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE

现在,假设我想要一个 _name_part = 'Whatever' 的序列,所以我输入:

SELECT get_value('Whatever');

如果序列constant_part_of_name_Whatever 不存在,我的函数应该创建它并取一个值;如果它存在,它应该只取一个值。但是,我创建了序列constant_part_of_name_sequencename

如何将变量的值放入序列定义中以使其工作?

【问题讨论】:

    标签: sql postgresql sequence plpgsql dynamic-sql


    【解决方案1】:

    当前接受的答案存在许多问题。最重要的是,它没有考虑到 schema

    改用:

    CREATE OR REPLACE FUNCTION get_value(_name_part text)
      RETURNS bigint AS
    $func$
    DECLARE
       _seq text  := 'constant_part_of_name_' || _name_part;
    BEGIN
    
    CASE (SELECT c.relkind = 'S'::"char"
          FROM   pg_namespace n
          JOIN   pg_class     c ON c.relnamespace = n.oid
          WHERE  n.nspname = current_schema()  -- or provide your schema!
          AND    c.relname = _seq)
    WHEN TRUE THEN           -- sequence exists
        -- do nothing
    WHEN FALSE THEN          -- not a sequence
       RAISE EXCEPTION '% is not a sequence!', _seq;
    ELSE                     -- sequence does not exist, name is free
       EXECUTE format('CREATE SEQUENCE %I MINVALUE 6000000 INCREMENT BY 1', _seq);
    END CASE;
    
    RETURN nextval(_seq);
    
    END
    $func$  LANGUAGE plpgsql;
    

    SQL Fiddle.

    要点

    • concat() 仅在可以涉及 NULL 值时才有用。我假设你不想通过NULL

    • VOLATILE 是默认值,因此只是噪音。

    • 如果要在 NULL 输入上返回 NULL,请添加 STRICT

    【讨论】:

    • 我的函数很简单,因为我运行的数据库并不复杂。但是,从一开始就正确地写东西绝对是一个好习惯,所以我将使用您的答案并查看您链接中提供的主题。感谢您的回答。
    • @spoko:我再次更新了函数。速度和优雅度稍高一些。添加了小提琴和更多解释。
    • 通过pg_* 视图检查存在时存在固有的竞争条件。无论如何都没有必要,您可以只调用nextval(seq) 并在异常子句中处理不存在错误。如果另一个会话忙于删除或创建相同的序列,至少 nextval 会阻塞。
    • @DanielVérité:引发异常不会消除竞争条件。并且,quoting the manualA block containing an EXCEPTION clause is significantly more expensive to enter and exit than a block without one. Therefore, don't use EXCEPTION without need. 会扩大比赛的时间范围。可以将FOR SHARE 添加到SELECT 以防止并发DELETE(从未尝试过)。但是没有针对并发CREATE SEQUENCE 的药物。最好保持小额交易...
    • @ErwinBrandstetter 感谢您的参与。我知道已经过去了一段时间,但我还是希望你能看到这一点。上面的代码在 Postgres 9.3 上工作得很好。但是,当我在 Postgres 8.3.23 中运行它时,我得到 syntax error at or near "CASE" 和 ^ 标记指向代码中 CASE 单词的开头。您知道我需要添加/删除/更改什么才能使其正常工作吗?
    【解决方案2】:

    试试这个。希望这对你有用。

    CREATE OR REPLACE FUNCTION get_value(_name_part character varying) RETURNS INTEGER AS
    $BODY$
    DECLARE
        result bigint;
        sequencename character varying(50);
        v_sql character varying;
    BEGIN
        sequencename = CONCAT('constant_part_of_name_', _name_part);
        IF((SELECT CAST(COUNT(*) AS INTEGER) FROM pg_class WHERE relname LIKE sequencename) = 0)
         THEN
         v_sql :=  'CREATE SEQUENCE '||sequencename||' 
           MINVALUE 6000000
           INCREMENT BY 1;';
           EXECUTE  v_sql;
        END IF;
        SELECT nextval(sequencename) INTO result ;
        RETURN result;
    END;
    $BODY$
      LANGUAGE plpgsql VOLATILE
    

    【讨论】:

    • 它做到了,这就是我想要的。但是,我允许自己稍微改进您的答案,希望您不要介意;)。
    • 这个函数还有很多问题。我提供了另一个答案。
    【解决方案3】:
    CREATE OR REPLACE FUNCTION get_value(_name_part character varying) RETURNS INTEGER AS
    $BODY$
    DECLARE
        result bigint;
        sequencename character varying(50);
    BEGIN
        sequencename = CONCAT('constant_part_of_name_', _name_part);
        IF  (select exists(SELECT relname FROM pg_class c WHERE c.relkind = 'S' and relname = ''''||sequencename||'''') = false )
        THEN
        execute 'CREATE SEQUENCE '||sequencename||'MINVALUE 6000000 INCREMENT BY 1';
        else
        END IF;
        execute 'SELECT nextval('''||sequencename||''')' INTO result;
        RETURN result;
    END;
    $BODY$
    LANGUAGE plpgsql VOLATILE
    

    【讨论】:

      猜你喜欢
      • 2014-10-24
      • 2014-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-16
      • 2014-02-08
      • 1970-01-01
      相关资源
      最近更新 更多