复制具有单独序列的连续列的表

Joh*_*ohn 6 postgresql database-design primary-key sequence dynamic-sql

在创建列管理工具时,我遇到了在 PostgreSQL 中快速复制表的需要,因此我不会使用非测试表来测试新工具。为了有效地测试我最终打算在表上使用的新列工具,parts我创建了这个新工具来复制parts,这样我最终会得到一个parts1表。当我以为我终于解决了所有问题时,当列工具删除表时,我遇到了以下错误:

错误:无法删除表部分,因为其他对象依赖于它详细信息:表 parts1 列 id 的默认值取决于序列 parts_id_seq1

我花了一天的大部分时间来研究这个解决方案,所以简而言之,我可以简单地使用字符串函数来重命名变量以将表与表SEQUENCE_NAME分离,还是这是一个比这更复杂的问题?这是查询:partsparts

DO $$
  DECLARE
    SEQUENCE_NAME VARCHAR;
  BEGIN
    SELECT s.relname INTO SEQUENCE_NAME
    FROM pg_class AS s JOIN pg_depend d ON d.objid = s.oid 
    INNER JOIN pg_class AS t ON d.objid = s.oid AND d.refobjid = t.oid 
    INNER JOIN pg_attribute AS a ON (d.refobjid, d.refobjsubid) = (a.attrelid, a.attnum) 
    INNER JOIN pg_namespace AS n ON n.oid = s.relnamespace 
    WHERE s.relkind = 'S' 
    AND n.nspname = 'public' 
    AND t.relname='parts';

    LOCK TABLE parts;
    CREATE TABLE parts1 (LIKE parts INCLUDING ALL);
    INSERT INTO parts1 SELECT * FROM parts;
    PERFORM setval(SEQUENCE_NAME::regclass, (SELECT max(id) FROM parts)+1);
  END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 7

要创建尽可能接近的副本,请使用INCLUDING ALLwith CREATE TABLE .. (LIKE ..),因为可以有任意数量的具有您显然想要复制的默认值的列。

您只希望serial列获得自己的独立序列,这很有意义,并且可能应该是一开始的默认行为。

Postgres 10通过添加IDENTITY符合 SQL 标准的列“修复”了这个问题,这些列具有内部的、专用的序列,并且按照CREATE TABLE .. (LIKE ..). 手册:

INCLUDING IDENTITY

复制的列定义的任何标识规范都将被复制。为新表的每个标识列创建一个新序列,与旧表关联的序列分开

大胆强调我的。现有的串行列保持不变。考虑更换serial色谱柱。看:

复制带有(或不带有)serial列的表的函数

serial虽然仍然涉及任何列,但此函数应该完成这项工作:

使用新的给定名称和独立serial(如果有)复制任何给定表(必须存在)。
数据不包括在内,复制它也很简单。

CREATE OR REPLACE FUNCTION f_copy_tbl(_tbl regclass, _newtbl text, _newschema text = NULL)
  RETURNS void
  LANGUAGE plpgsql VOLATILE AS
$func$
DECLARE
   _newtbl_qual_n_quot text;
   _sql text;
BEGIN
   -- If target schema is not given, default to schema of source
   IF _newschema IS NULL THEN      
      SELECT INTO _newschema  relnamespace::regnamespace::text
      FROM   pg_class
      WHERE  oid = _tbl;
   END IF;
   
   _newtbl_qual_n_quot := format('%I.%I', _newschema, _newtbl);
   
   -- Copy table
   EXECUTE format('CREATE TABLE %s (LIKE %s INCLUDING ALL);', _newtbl_qual_n_quot, _tbl);

   -- Fix serial columns, if any
   SELECT INTO _sql
          string_agg('CREATE SEQUENCE ' || seq_qual_n_quot, E';\n') || E';\n'
       || string_agg(format('ALTER SEQUENCE %s OWNED BY %s.%I'
                           , seq_qual_n_quot, _newtbl_qual_n_quot, a.attname), E';\n') || E';\n'
       || 'ALTER TABLE ' || _newtbl_qual_n_quot || E'\n  '
       || string_agg(format($$ALTER %I SET DEFAULT nextval('%s'::regclass)$$
                          , a.attname, seq_qual_n_quot), E'\n, ')
   FROM   pg_attribute  a
   JOIN   pg_attrdef    ad ON ad.adrelid = a.attrelid
                          AND ad.adnum   = a.attnum
        , format('%I.%I'
               , _newschema
               , _newtbl || '_' || a.attname || '_seq'
                ) AS seq_qual_n_quot  -- new seq name, qualified & quoted where necessary
   WHERE  a.attrelid = _tbl
   AND    a.attnum > 0
   AND    NOT a.attisdropped
   AND    a.atttypid = ANY ('{int,int8,int2}'::regtype[])
   AND    pg_get_expr(ad.adbin, ad.adrelid) = 'nextval('''
            || (pg_get_serial_sequence (a.attrelid::regclass::text, a.attname))::regclass
            || '''::regclass)'
   ;

   IF _sql IS NOT NULL THEN
      EXECUTE _sql;
   END IF;
END
$func$;
Run Code Online (Sandbox Code Playgroud)

称呼:

SELECT f_copy_tbl('tbl', 'tbl1');
Run Code Online (Sandbox Code Playgroud)

或者:

SELECT f_copy_tbl('myschema.tbl', 'CpY_tbl_odd_name', 'other_schema');
Run Code Online (Sandbox Code Playgroud)

生成并执行以下形式的 SQL 代码:

CREATE TABLE tbl1 (LIKE tbl INCLUDING ALL);
-- only if there are serial columns:
CREATE SEQUENCE tbl1_tbl_id_seq;     -- one line per serial type ...
CREATE SEQUENCE "tbl1_Odd_COL_seq";  -- .. two in this example
ALTER SEQUENCE tbl1_tbl_id_seq OWNED BY tbl1.tbl_id;
ALTER SEQUENCE "tbl1_Odd_COL_seq" OWNED BY tbl1."Odd_COL";
ALTER TABLE tbl1
  ALTER tbl_id SET DEFAULT nextval('tbl1_tbl_id_seq'::regclass)
, ALTER "Odd_COL" SET DEFAULT nextval('"tbl1_Odd_COL_seq"'::regclass);
Run Code Online (Sandbox Code Playgroud)
  • 源(第一个参数)必须是表、视图、物化视图、复合类型或外部表。可选模式限定。

  • 第二个参数是新表名称。

  • 第三个参数是新表的模式。如果未给出,则默认为源的架构。

  • 系统列pg_attrdef.adsrc已在 Postgres 12 中删除。pg_get_expr(ad.adbin, ad.adrelid)按照手册中的说明使用它也可以在旧版本中使用。

  • 只有serial列有自己的顺序。其他列默认值按原样复制 - 包括nextval()从不属于该列的序列或与serial.

  • 该函数可以安全地防止 SQL 注入,并且可以使用任意表名和列名。

db<>在这里
摆弄旧的sqlfiddle