【问题标题】:Safely rename tables using serial primary key columns使用串行主键列安全地重命名表
【发布时间】:2013-01-16 23:09:46
【问题描述】:

我知道使用 SERIAL 主键的 PostgreSQL 表最终会由 PostgreSQL 创建隐式索引、序列和约束。问题是在重命名表时如何重命名这些隐式对象。下面是我试图通过最后的具体问题来解决这个问题。

给定一个表格,例如:

CREATE TABLE foo (
    pkey SERIAL PRIMARY KEY,
    value INTEGER
);

Postgres 输出:

注意:CREATE TABLE 将为串行列“foo.pkey”创建隐式序列“foo_pkey_seq”
注意:CREATE TABLE / PRIMARY KEY 将为表“foo”创建隐式索引“foo_pkey”
查询成功返回,52 毫秒无结果。

pgAdmin III SQL 窗格显示了表的以下 DDL 脚本(已整理):

CREATE TABLE foo (
  pkey serial NOT NULL,
  value integer,
  CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE foo OWNER TO postgres;

现在重命名表格:

ALTER table foo RENAME TO bar;

查询成功返回,17 毫秒内没有结果。

pgAdmin III:

CREATE TABLE bar (
  pkey integer NOT NULL DEFAULT nextval('foo_pkey_seq'::regclass),
  value integer,
  CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE bar OWNER TO postgres;

注意额外的DEFAULT nextval('foo_pkey_seq'::regclass),,这意味着重命名表不会重命名主键的序列,但现在我们有了这个显式的nextval()

现在重命名序列:

我想保持数据库命名一致,所以我尝试了:

ALTER SEQUENCE foo_pkey_seq RENAME TO bar_pkey_seq;

查询成功返回,17 毫秒内没有结果。

pgAdmin III:

CREATE TABLE bar (
  pkey serial NOT NULL,
  value integer,
  CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE bar OWNER TO postgres;

DEFAULT nextval('foo_pkey_seq'::regclass), 不见了。

问题

  1. 为什么DEFAULT nextval('foo_pkey_seq'::regclass) 语句会出现又消失?
  2. 有没有办法重命名表并同时重命名主键序列?
  3. 在客户端连接到数据库时重命名表然后排序是否安全,是否存在任何并发问题?
  4. postgres 如何知道使用哪个序列?内部是否使用了数据库触发器?除了表格和序列之外,还有什么要重命名的吗?
  5. 主键创建的隐式索引呢?应该改名吗?如果是这样,那该怎么做?
  6. 上面的约束名称呢?它仍然是foo_pkey。如何重命名约束?

【问题讨论】:

  • 我的猜测是,命名一个序列<<tablename>>_pkey_seq 具有神奇的语法意义,并且 Postgres 知道使用一个这样命名的序列作为表的主键序列并省略明确列出它作为默认值表的主键列的值。不过,我实际上并不知道这一点,也没有任何证据。如果到那时这个问题还没有答案,下班后会进一步调查。

标签: sql postgresql database-design ddl


【解决方案1】:

serial 不是实际的数据类型。 The manual states:

数据类型smallserialserialbigserial不是真正的类型, 但仅仅是创建唯一标识符列的符号方便

通过所有这些来解析伪数据类型:

  • 创建一个名为tablename_colname_seq的序列

  • 创建类型为integer 的列(或int2 / int8 分别对应smallserial / bigserial

  • 创建专栏NOT NULL DEFAULT nextval('tablename_colname_seq')

  • 使列拥有序列,以便自动删除它

系统知道您是手动完成所有这些操作还是通过伪数据类型serial 完成的。 pgAdmin 检查列出的功能,如果所有功能都满足,则反向工程 DDL 脚本将使用匹配的serial 类型进行简化。如果不满足其中一个特征,则不会发生这种简化。这就是 pgAdmin 所做的事情。对于基础目录表,它都是一样的。没有 serial 这样的类型。

无法自动重命名拥有的序列。你可以运行:

ALTER SEQUENCE ... RENAME TO ...

像你一样。系统本身并不关心 nameDEFAULT 列存储 OID ('foo_pkey_seq'::regclass),您可以更改序列的名称而不会破坏它 - OID 保持不变。数据库中的外键和类似引用也是如此。

主键的隐式索引绑定到PK约束的名称,如果你改变表的名称,它不会改变。 In Postgres 9.2 or later you can use

ALTER TABLE ... RENAME CONSTRAINT ..

也要纠正它。

也可以参考表名来命名索引。 Similar procedure:

ALTER INDEX .. RENAME TO  ..

您可以对表名进行各种非正式引用。系统无法强制重命名可以命名的对象。它并不在乎。

当然,您不想使引用这些名称的 SQL 代码无效。显然,您不想在应用程序逻辑引用它们时更改名称。通常这对于索引、序列或约束的名称不会有问题,因为它们通常不会被名称引用。

Postgres 在重命名对象之前也会获取对象的锁定。因此,如果有并发事务打开并且对相关对象具有任何类型的锁定,您的RENAME 操作将停止,直到这些事务提交或回滚。

系统目录和 OID

数据库架构存储在系统架构pg_catalog 中的系统目录表中。 All details in the manual here. 如果你不知道自己在做什么,你根本不应该弄乱那些桌子。一个错误的举动,你可以打破你的数据库。 Use the DDL commands Postgres provides.

对于一些最重要的表,Postgres 提供了object identifier types 和类型转换来快速获取 OID 的名称,反之亦然。喜欢:

SELECT 'foo_pkey_seq'::regclass

如果架构名称在 search_path 中并且表名称是唯一的,那么您将得到相同的结果:

SELECT oid FROM pg_class WHERE relname = 'foo_pkey_seq';

大多数目录表的主键是oid,在内部,大多数引用使用 OID。

【讨论】:

  • "ALTER TABLE ... RENAME CONSTRAINT .." 需要 PostgreSQL 9.2+ 也许这对我刚刚调试了一个小时有帮助:-P 很好的答案!
猜你喜欢
  • 2021-07-04
  • 2012-10-04
  • 1970-01-01
  • 2020-02-01
  • 2011-02-11
  • 2017-09-21
  • 1970-01-01
  • 1970-01-01
  • 2015-04-13
相关资源
最近更新 更多