我的数据库布局需要为每个新客户创建新架构。目前我使用网上发现的内部功能并进行了一些修改。
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语句(或其他可靠的方法)来获取新创建的表的序列的新副本(甚至是复制整个模式的另一种可靠方法)?
答案 0 :(得分:5)
与旧序列的连接来自所涉及列的普通默认值。我引用the manual here:
复制的列定义的默认表达式仅为 如果指定
INCLUDING DEFAULTS
则复制。默认行为是 排除默认表达式,导致复制的列中的 新表的默认值为null。
因为您使用
创建新表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
列。
答案 1 :(得分:5)
所以经过一番思考后,我继续更新我在第一篇文章中提到的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建议的版本再次使用dump / hack转储文件/恢复转储文件,这在论坛中常见。
如果是专用服务器,它可以工作,如果是共享主机(或需要较少依赖外部脚本),内部功能的方式似乎更好。
答案 2 :(得分:1)
您可以简单地备份架构,然后在数据库中对其重命名,然后还原已备份的文件。