【问题标题】:Check if sequence exists in Postgres (plpgsql)检查 Postgres 中是否存在序列(plpgsql)
【发布时间】:2012-08-08 00:05:35
【问题描述】:

我正在尝试在存储过程中测试一个序列是否已经存在。

IF EXISTS SEQUENCE seq_name
    RAISE EXCEPTION 'sequence % already exists!', seq_name
END IF;

我已经尝试了上面 sn-p 的几种变体,但没有运气。我一定是给 Google 提供了错误的术语,因为我似乎找不到任何关于该主题的内容。任何帮助表示赞赏!

【问题讨论】:

    标签: postgresql stored-procedures plpgsql


    【解决方案1】:

    我不确定为什么必须检查序列的存在的实际意图。如果目标是在创建序列之前检查序列是否存在,则可以使用 PostgreSQL 中的IF NOT EXISTS 条件:

    CREATE SEQUENCE IF NOT EXISTS 'name'
    

    https://www.postgresql.org/docs/9.5/static/sql-createsequence.html

    【讨论】:

      【解决方案2】:

      如何使用信息架构:

      SELECT COUNT(*) 
      FROM information_schema.sequences 
      WHERE sequence_schema=? AND sequence_name=?
      

      【讨论】:

        【解决方案3】:
        select relname, relnamespace
        from pg_class join pg_catalog.pg_namespace n ON n.oid = pg_class.relnamespace
        where n.nspname='metastore_1' and relname='updater_state_id_seq';
        

        结果:

               relname        | relnamespace 
        -------------------------------------
         updater_state_id_seq |        32898
        

        此查询可以检查模式中是否存在序列。

        【讨论】:

          【解决方案4】:

          更新:在 Postgres 9.4 中使用 to_regclass() 简单地测试存在性变得更加简单:

          SELECT to_regclass('schema_name.table_name');
          

          但请阅读详细信息:

          功能齐全

          您需要检查与名称冲突的任何类似表的对象,而不仅仅是序列。

          如果名称可用,此函数会创建一个新序列,并在其他情况下分别发出有意义的NOTICE / WARNING / EXCEPTION

          CREATE OR REPLACE FUNCTION f_create_seq(_seq text, _schema text = NULL)
            RETURNS void AS
          $func$
          DECLARE
             _fullname text := format('%I.%I', COALESCE(_schema,current_schema),_seq);
             _relkind "char" := (SELECT c.relkind
                                 FROM   pg_namespace n
                                 JOIN   pg_class c ON c.relnamespace = n.oid
                                 WHERE  n.nspname = COALESCE(_schema, current_schema)
                                 AND    c.relname = _seq);
          BEGIN
             IF _relkind IS NULL THEN   -- name is free
                EXECUTE 'CREATE SEQUENCE ' || _fullname;
                RAISE NOTICE 'New sequence % created.', _fullname;
          
             ELSIF _relkind = 'S' THEN  -- 'S' = sequence
                IF has_sequence_privilege(_fullname, 'USAGE') THEN
                   RAISE WARNING 'Sequence % already exists.', _fullname;
                ELSE
                   RAISE EXCEPTION
                     'Sequence % already exists but you have no USAGE privilege.'
                   , _fullname;
                END IF;
          
             ELSE
                RAISE EXCEPTION 'A(n) "%" named % already exists.'
                -- Table-like objects in pg 9.4:
                -- www.postgresql.org/docs/current/static/catalog-pg-class.html
                   , CASE _relkind WHEN 'r' THEN 'ordinary table'
                                   WHEN 'i' THEN 'index'
                                -- WHEN 'S' THEN 'sequence'  -- impossible here
                                   WHEN 'v' THEN 'view'
                                   WHEN 'm' THEN 'materialized view'
                                   WHEN 'c' THEN 'composite type'
                                   WHEN 't' THEN 'TOAST table'
                                   WHEN 'f' THEN 'foreign table'
                                   ELSE 'unknown object' END
                   , _fullname;
             END IF;
          END
          $func$  LANGUAGE plpgsql;
          
          COMMENT ON FUNCTION f_create_seq(text, text) IS
          'Create sequence if name is free.
          RAISE NOTICE on successful creation.
          RAISE WARNING if it already exists.
          RAISE EXCEPTION if it already exists and current user lacks USAGE privilege.
          RAISE EXCEPTION if object of a different kind occupies the name.
          $1 _seq    .. sequence name 
          $2 _schema .. schema name (optional; default is CURRENT_SCHEMA)';
          

          呼叫:

          SELECT f_create_seq('myseq', 'myschema');
          

          或者:

          SELECT f_create_seq('myseq1');  -- defaults to current schema
          

          解释

          • 还要阅读代码末尾的函数注释。

          • 在 Postgres 9.1+ 中工作。对于旧版本,您只需要替换format() - 即可防御 SQL 注入。详情:

          • 两个单独的参数允许任何模式中的序列独立于当前search_path,还允许quote_ident() 完成其工作。 quote_ident() 使用模式限定名称失败 - 会模棱两可。

          • schema 参数有一个默认值,因此您可以在调用中省略它。如果没有给出模式,则函数默认为current_schemaPer documentation:

            current_schema 返回位于 搜索路径(如果搜索路径为空,则为空值)。这是 将用于任何表或其他命名对象的模式 是在没有指定目标架构的情况下创建的。

          • pgclass.relkind in the manual 的类型列表。

          • PostgreSQL error codes.

          【讨论】:

            【解决方案5】:

            如果您确定名称只能对序列有效(即,您确信它不会用于普通表、索引、视图、复合类型、TOAST表或外部表),并且您不关心多个模式。换句话说,它适用于大多数常见情况,但并不完全严格。

            如果您想测试该名称的 sequence 是否存在于特定的 schema 中,这应该可以:

            -- Clear the search path so that the regclass of the sequence
            -- will be schema-qualified.
            SET search_path = '';
            -- Do your conditional code.
            IF EXISTS (SELECT * FROM pg_class
                         WHERE relkind = 'S'
                           AND oid::regclass::text = 'public.' || quote_ident(seq_name))
              THEN
                RAISE EXCEPTION 'sequence public.% already exists!', seq_name
            END IF;
            -- Restore the normal search path.
            RESET search_path;
            

            【讨论】:

            • 什么是oid::regclass::text?在我的情况下,relkind 也是“S” - 大写,但是对于表格 - 'r' 小写。
            • 另外经过测试,我发现quote_ident 不适用于不存在的序列名称。
            • @Evgeny:关于转换为regclass,然后转换为text,请参阅此页面以获取有关regclass 的信息postgresql.org/docs/9.2/interactive/datatype-oid.html -- text 只是一个字符串。抱歉,我弄错了 relkind 比较——它确实是需要的大写字母 S。将修复答案。不过,我不明白您对quote_ident 所说的内容- 应该将其用于您传入的参数;该名称是否已经存在不会对 quote_ident 函数的行为产生任何影响。
            【解决方案6】:

            您应该能够查询 pg_class 表以查看 relname 是否存在。

            IF EXISTS (SELECT 0 FROM pg_class where relname = '<my sequence name here>' )
            THEN
              --stuff here
            END IF;
            

            【讨论】:

              猜你喜欢
              • 2021-04-16
              • 2021-11-04
              • 1970-01-01
              • 2012-06-29
              • 2016-04-23
              • 1970-01-01
              • 1970-01-01
              • 2012-01-22
              • 2021-12-31
              相关资源
              最近更新 更多