Joh*_*ohn 6 postgresql database-design primary-key sequence dynamic-sql
在创建列管理工具时,我遇到了在 PostgreSQL 中快速复制表的需要,因此我不会使用非测试表来测试新工具。为了有效地测试我最终打算在表上使用的新列工具,parts
我创建了这个新工具来复制parts
,这样我最终会得到一个parts1
表。当我以为我终于解决了所有问题时,当列工具删除表时,我遇到了以下错误:
错误:无法删除表部分,因为其他对象依赖于它详细信息:表 parts1 列 id 的默认值取决于序列 parts_id_seq1
我花了一天的大部分时间来研究这个解决方案,所以简而言之,我可以简单地使用字符串函数来重命名变量以将表与表SEQUENCE_NAME
分离,还是这是一个比这更复杂的问题?这是查询:parts
parts
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)
要创建尽可能接近的副本,请使用INCLUDING ALL
with 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 注入,并且可以使用任意表名和列名。
归档时间: |
|
查看次数: |
7642 次 |
最近记录: |