8

我的数据库布局需要为每个新客户创建新模式。目前我使用的是我在网上找到的内部函数并稍作修改。

CREATE FUNCTION copy_schema(
    source_schema character varying, 
    target_schema character varying, 
    copy_data boolean)
RETURNS integer AS
$BODY$
DECLARE
    t_ex integer := 0;
    s_ex integer := 0;
    src_table character varying;
    trg_table character varying;
BEGIN
    if (select 1 from pg_namespace where nspname = source_schema) THEN
        -- we have defined target schema
        s_ex := 1;
    END IF;

    IF (s_ex = 0) THEN
        -- no source schema exist
        RETURN 0;
    END IF;

    if (select 1 from pg_namespace where nspname = target_schema) THEN
        -- we have defined target schema need to sync all table layout
        t_ex := 1;
    ELSE
        EXECUTE 'CREATE SCHEMA '||target_schema||' AUTHORIZATION user';
    END IF;

    FOR src_table IN 
       SELECT table_name 
       FROM information_schema.TABLES 
       WHERE table_schema = source_schema
    LOOP
        trg_table := target_schema||'.'||src_table;
        EXECUTE 
            'CREATE TABLE ' || trg_table || ' (LIKE ' || source_schema || '.' || src_table || ' INCLUDING ALL)';
        IF (copy_data = true) THEN
            EXECUTE 'INSERT INTO ' || trg_table || '(SELECT * FROM ' || source_schema || '.' || src_table || ')';
        END IF;
    END LOOP;

    return t_ex;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

这个脚本的问题是新模式中的表继续使用源模式的序列。有没有办法使用 sql 语句(或其他可靠的方式)为新创建的表获取序列的新副本(或者甚至是复制整个架构的另一种可靠方式)?

4

3 回答 3

7

问题的根源

与旧序列的连接来自所涉及列的普通默认值。我在这里引用手册

INCLUDING DEFAULTS仅当指定时才会复制复制的列定义的默认表达式。默认行为是排除默认表达式,导致新表中复制的列具有空默认值。

由于您使用

INCLUDING ALL

和:

INCLUDING ALL是 的缩写形式INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS

..你得到相同的默认值。您可以nextval()在创建新表后排除默认值或显式更改默认值,包括新表。我不认为有任何中间立场。


使用转储/破解转储/恢复更简单

甚至是复制整个架构的另一种可靠方法

您可以使用以下方式转储架构的架构(相同的词,不同的含义)pg_dump

pg_dump $DB -p $PORT -n $SCHEMA -sf /var/lib/postgresql/your_name.pgsql

破解转储(意思是:在其上使用文本编辑器,或对其编写脚本):交换转储顶部的模式名称,以及所有其他出现在序列中的SET search_path和作为模式限定的序列,可能还有更多。如果您为模式选择了一个唯一的名称sed,那么用您最喜欢的工具(或vim或...)进行一次全局搜索和替换应该可以完成这项工作。

psql然后针对相同或任何其他数据库运行 SQL 脚本:

psql $DB -p $PORT -f /var/lib/postgresql/your_name.pgsql > /dev/null

与我第一次发布的相反,串行列仍然在转储中拆分(至少在 PostgreSQL 9.1.5 中)。SQL 脚本分别创建序列,将它们附加到序列列:

ALTER SEQUENCE seq OWNED BY tbl.col;

并单独设置默认值。

顺便说一句:当满足所有要求时,DDL 脚本中的 pgAdmin 逆向工程serial列的当前版本。

于 2012-09-24T20:27:11.323 回答
6

因此,经过一番思考,我继续更新我第一篇文章中提到的 sql 函数,所以现在它看起来像这样:

CREATE FUNCTION copy_schema(
    source_schema character varying, 
    target_schema character varying, 
    copy_data boolean)
RETURNS integer AS
$BODY$
DECLARE
    t_ex integer := 0;
    s_ex integer := 0;
    src_table character varying;
    trg_table character varying;
BEGIN
    if (select 1 from pg_namespace where nspname = source_schema) THEN
        -- we have defined target schema
        s_ex := 1;
    END IF;

    IF (s_ex = 0) THEN
        -- no source schema exist
        RETURN 0;
    END IF;

    if (select 1 from pg_namespace where nspname = target_schema) THEN
        -- we have defined target schema need to sync all table layout
        t_ex := 1;
    ELSE
        EXECUTE 'CREATE SCHEMA '||target_schema||' AUTHORIZATION user';
    END IF;

    FOR src_table IN 
        SELECT table_name 
        FROM information_schema.TABLES 
        WHERE table_schema = source_schema
    LOOP
        trg_table := target_schema||'.'||src_table;
        EXECUTE 'CREATE TABLE ' || trg_table || ' (LIKE ' || source_schema || '.' || src_table || ' INCLUDING ALL)';
        EXECUTE 'CREATE SEQUENCE ' || trg_table || '_id_seq OWNED BY '||trg_table || '.id';
        EXECUTE 'ALTER TABLE ' || trg_table || ' ALTER COLUMN id SET DEFAULT nextval('''|| trg_table || '_id_seq''::regclass)';
        IF (copy_data = true) THEN
            EXECUTE 'INSERT INTO ' || trg_table || '(SELECT * FROM ' || source_schema || '.' || src_table || ')';
        END IF;
    END LOOP;
    return t_ex;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

这对每个人来说都不是很通用的解决方案,但由于我在模式中的所有表都有名为 id 的串行字段,所以它适合我。

@erwin-brandstetter 建议的带有转储/破解转储文件/恢复转储文件的版本在论坛上通常被视为要走的路。

在专用服务器的情况下它可以工作,在共享主机的情况下(或需要较少依赖外部脚本),内部功能的方式似乎更好。

于 2012-09-24T22:15:52.607 回答
2

您可以简单地备份模式,然后在数据库中重命名它,然后恢复备份的文件。

于 2020-12-07T17:49:38.453 回答