ALTER TABLE
ALTER TABLE — 更改一个表的定义
大纲
ALTER TABLE [ IF EXISTS ] [ ONLY ]name[ * ]action[, ... ] ALTER TABLE [ IF EXISTS ] [ ONLY ]name[ * ] RENAME [ COLUMN ]column_nameTOnew_column_nameALTER TABLE [ IF EXISTS ] [ ONLY ]name[ * ] RENAME CONSTRAINTconstraint_nameTOnew_constraint_nameALTER TABLE [ IF EXISTS ]nameRENAME TOnew_nameALTER TABLE [ IF EXISTS ]nameSET SCHEMAnew_schemaALTER TABLE ALL IN TABLESPACEname[ OWNED BYrole_name[, ... ] ] SET TABLESPACEnew_tablespace[ NOWAIT ] ALTER TABLE [ IF EXISTS ]nameATTACH PARTITIONpartition_nameFOR VALUESpartition_bound_specALTER TABLE [ IF EXISTS ]nameDETACH PARTITIONpartition_name其中action为以下之一: ADD [ COLUMN ] [ IF NOT EXISTS ]column_namedata_type[ COLLATEcollation] [column_constraint[ ... ] ] DROP [ COLUMN ] [ IF EXISTS ]column_name[ RESTRICT | CASCADE ] ALTER [ COLUMN ]column_name[ SET DATA ] TYPEdata_type[ COLLATEcollation] [ USINGexpression] ALTER [ COLUMN ]column_nameSET DEFAULTexpressionALTER [ COLUMN ]column_nameDROP DEFAULT ALTER [ COLUMN ]column_name{ SET | DROP } NOT NULL ALTER [ COLUMN ]column_nameADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ (sequence_options) ] ALTER [ COLUMN ]column_name{ SET GENERATED { ALWAYS | BY DEFAULT } | SETsequence_option| RESTART [ [ WITH ]restart] } [...] ALTER [ COLUMN ]column_nameDROP IDENTITY [ IF EXISTS ] ALTER [ COLUMN ]column_nameSET STATISTICSintegerALTER [ COLUMN ]column_nameSET (attribute_option=value[, ... ] ) ALTER [ COLUMN ]column_nameRESET (attribute_option[, ... ] ) ALTER [ COLUMN ]column_nameSET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } ADDtable_constraint[ NOT VALID ] ADDtable_constraint_using_indexALTER CONSTRAINTconstraint_name[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] VALIDATE CONSTRAINTconstraint_nameDROP CONSTRAINT [ IF EXISTS ]constraint_name[ RESTRICT | CASCADE ] DISABLE TRIGGER [trigger_name| ALL | USER ] ENABLE TRIGGER [trigger_name| ALL | USER ] ENABLE REPLICA TRIGGERtrigger_nameENABLE ALWAYS TRIGGERtrigger_nameDISABLE RULErewrite_rule_nameENABLE RULErewrite_rule_nameENABLE REPLICA RULErewrite_rule_nameENABLE ALWAYS RULErewrite_rule_nameDISABLE ROW LEVEL SECURITY ENABLE ROW LEVEL SECURITY FORCE ROW LEVEL SECURITY NO FORCE ROW LEVEL SECURITY CLUSTER ONindex_nameSET WITHOUT CLUSTER SET WITH OIDS SET WITHOUT OIDS SET TABLESPACEnew_tablespaceSET { LOGGED | UNLOGGED } SET (storage_parameter[=value] [, ... ] ) RESET (storage_parameter[, ... ] ) INHERITparent_tableNO INHERITparent_tableOFtype_nameNOT OF OWNER TO {new_owner| CURRENT_USER | SESSION_USER } REPLICA IDENTITY { DEFAULT | USING INDEXindex_name| FULL | NOTHING } 而table_constraint_using_index为: [ CONSTRAINTconstraint_name] { UNIQUE | PRIMARY KEY } USING INDEXindex_name[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
描述
ALTER TABLE 更改现有表的定义。下面描述了若干子形式。请注意,各子形式所需的锁级别可能不同。除非另有明确说明,否则将获取 ACCESS EXCLUSIVE 锁。给出多个子命令时,获取的锁将是其中任一子命令所需的最严格级别。
ADD COLUMN [ IF NOT EXISTS ]该形式使用与 CREATE TABLE 相同的语法向表添加新列。如果指定
IF NOT EXISTS且已存在同名列,则不会抛出错误。DROP COLUMN [ IF EXISTS ]该形式从表中删除一列。涉及该列的索引和表约束也会自动删除。如果删除该列会使某个引用它的多元统计信息只剩下一列数据,那么该统计信息也会被移除。如果表外有任何对象依赖于该列,例如外键引用或视图,你就需要指定
CASCADE。如果指定了IF EXISTS而该列不存在,则不会报错;此时会发出一条提示。SET DATA TYPE该形式更改表中某一列的类型。涉及该列的索引和简单表约束会通过重新解析最初提供的表达式,自动转换为使用新的列类型。可选的
COLLATE子句为新列指定排序规则;如果省略,则使用新列类型的默认排序规则。可选的USING子句指定如何根据旧值计算新列的值;如果省略,则默认转换与从旧数据类型到新数据类型的赋值类型转换相同。如果从旧类型到新类型不存在隐式或赋值类型转换,则必须提供USING子句。SET/DROP DEFAULT这些形式为列设置或移除默认值。默认值只会应用于后续的
INSERT或UPDATE命令;它们不会导致表中已有的行发生变化。SET/DROP NOT NULL这些形式更改列是否被标记为允许空值,或拒绝空值。只有当列不包含空值时,才能使用
SET NOT NULL。如果该表是分区,并且某列在父表中标记为
NOT NULL,则不能对该列执行DROP NOT NULL。要从所有分区删除NOT NULL约束,请在父表上执行DROP NOT NULL。即使父表没有NOT NULL约束,仍然可以按需将此类约束添加到单个分区;也就是说,即使父表允许空值,子表也可以禁止空值,但反之则不行。ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITYSET GENERATED { ALWAYS | BY DEFAULT }DROP IDENTITY [ IF EXISTS ]这些形式更改列是否为标识列,或更改现有标识列的生成属性。详情请参见 CREATE TABLE。
如果指定了
DROP IDENTITY IF EXISTS,而该列不是标识列,则不会报错;此时会发出一条提示。SETsequence_optionRESTART这些形式更改支撑现有标识列的序列。
sequence_option是 ALTER SEQUENCE 支持的选项,例如INCREMENT BY。SET STATISTICS该形式为后续 ANALYZE 操作设置每列的统计信息收集目标。目标可以设置在 0 到 10000 范围内;也可以将其设置为 -1,以恢复使用系统默认的统计目标(default_statistics_target)。有关 PostgreSQL 查询规划器使用统计信息的更多信息,请参见第 14.2 节。
SET STATISTICS会获取一个SHARE UPDATE EXCLUSIVE锁。SET (attribute_option=value[, ... ] )RESET (attribute_option[, ... ] )该形式设置或重置每属性选项。目前定义的每属性选项只有
n_distinct和n_distinct_inherited,它们会覆盖后续 ANALYZE 操作所作的非重复值数量估计。n_distinct影响表本身的统计信息,而n_distinct_inherited影响为表及其继承子表收集的统计信息。当设置为正值时,ANALYZE会假定该列恰好包含指定数量的非空非重复值。当设置为负值时(该值必须大于或等于 -1),ANALYZE会假定列中非空非重复值的数量与表大小成线性关系;具体数量通过将估计的表大小乘以给定数值的绝对值计算。例如,-1 表示列中的所有值都不同,而 -0.5 表示平均每个值出现两次。当表大小随时间变化时,这可能很有用,因为只有在查询规划时才会执行与表中行数相乘的操作。指定 0 可恢复正常估计非重复值数量。有关 PostgreSQL 查询规划器使用统计信息的更多信息,请参见第 14.2 节。更改每个属性的选项会获取一个
SHARE UPDATE EXCLUSIVE锁。SET STORAGE该形式为列设置存储模式。这控制该列是行内保存还是保存在辅助 TOAST 表中,以及是否压缩数据。对于
integer等定长值,必须使用PLAIN,数据以行内且未压缩的形式保存。MAIN用于行内的可压缩数据。EXTERNAL用于行外保存的未压缩数据,EXTENDED用于行外保存的压缩数据。对于支持非PLAIN存储的大多数数据类型,EXTENDED是默认值。使用EXTERNAL会使非常大的text和bytea值上的子字符串操作运行得更快,但会增加存储空间。请注意,SET STORAGE本身不会更改表中的任何内容,它只会设置今后更新表时采用的策略。更多信息请参见第 67.2 节。ADDtable_constraint[ NOT VALID ]该形式使用与 CREATE TABLE 相同的约束语法向表添加新约束,另外还提供
NOT VALID选项;目前该选项只允许用于外键和 CHECK 约束。通常,该形式会扫描表,以验证表中所有现有行都满足新约束。但如果使用
NOT VALID选项,则会跳过这一可能耗时很长的扫描。后续插入或更新仍会强制执行该约束(也就是说,对于外键,除非被引用表中存在匹配行,否则操作会失败;对于 CHECK 约束,除非新行符合指定检查条件,否则操作会失败)。但是,在使用VALIDATE CONSTRAINT选项验证约束之前,数据库不会假定该约束对表中的所有行都成立。有关使用NOT VALID选项的更多信息,请参见下文注解。尽管大多数形式的
ADD需要table_constraintACCESS EXCLUSIVE锁,ADD FOREIGN KEY只需要SHARE ROW EXCLUSIVE锁。请注意,ADD FOREIGN KEY除了在声明约束的表上获取锁之外,还会在被引用的表上获取SHARE ROW EXCLUSIVE锁。ADDtable_constraint_using_index该形式基于一个现有唯一索引向表中添加新的
PRIMARY KEY或UNIQUE约束。该约束将包含该索引的全部列。该索引不能包含表达式列,也不能是部分索引。此外,它必须是使用默认排序顺序的 B-树索引。这些限制保证该索引等价于通过常规
ADD PRIMARY KEY或ADD UNIQUE命令构建出来的索引。如果指定了
PRIMARY KEY,而该索引的列尚未被标记为NOT NULL,则此命令会尝试对每个这样的列执行ALTER COLUMN SET NOT NULL。这需要进行一次全表扫描,以验证这些列不包含空值。在其他情况下,这都是一个快速操作。如果提供了约束名,则索引会被重命名以匹配该约束名;否则,约束将使用与索引相同的名称。
执行此命令后,该索引会被该约束“拥有”,就像它是通过常规
ADD PRIMARY KEY或ADD UNIQUE命令构建出来的一样。特别是,删除该约束也会使该索引一并消失。注意
在需要添加新约束且不希望长时间阻塞表更新的情况下,使用现有索引添加约束可能很有用。为此,请使用
CREATE INDEX CONCURRENTLY创建索引,然后使用此语法将其安装为正式约束。请参见下面的示例。ALTER CONSTRAINT该形式更改先前创建的约束的属性。目前只能更改外键约束。
VALIDATE CONSTRAINT该形式通过扫描表来验证先前以
NOT VALID创建的外键或 CHECK 约束,确保不存在不满足约束的行。如果约束已经标记为有效,则不执行任何操作。(有关此命令用途的说明,请参见下文注解。)此命令会获取一个
SHARE UPDATE EXCLUSIVE锁。DROP CONSTRAINT [ IF EXISTS ]该形式从表中删除指定的约束。如果指定
IF EXISTS且约束不存在,则不会抛出错误;在这种情况下会发出通知。DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER这些形式配置属于表的触发器的触发。已禁用的触发器仍为系统所知,但在触发事件发生时不会执行。对于延迟触发器,会在事件发生时检查启用状态,而不是在实际执行触发器函数时检查。可以按名称指定单个触发器,或指定表上的所有触发器,或仅指定用户触发器,从而禁用或启用触发器(最后一种选项不包括内部生成的约束触发器,例如用于实现外键约束或可延迟唯一性约束和排他约束的触发器)。禁用或启用内部生成的约束触发器需要超级用户权限;应谨慎操作,因为如果不执行触发器,当然无法保证约束的完整性。触发器触发机制还会受到配置变量 session_replication_role 的影响。简单启用的触发器会在复制角色为“origin”(默认值)或“local”时触发。配置为
ENABLE REPLICA的触发器只会在会话处于“replica”模式时触发,配置为ENABLE ALWAYS的触发器则无论当前复制模式为何都会触发。此命令会获取一个
SHARE ROW EXCLUSIVE锁。DISABLE/ENABLE [ REPLICA | ALWAYS ] RULE这些形式配置属于表的重写规则是否应用。禁用的规则仍然为系统所知,但在查询重写期间不会应用。语义与禁用/启用触发器相同。对于
ON SELECT规则,此配置将被忽略,这些规则始终会被应用,以确保视图在当前会话处于非默认复制角色时仍然正常工作。DISABLE/ENABLE ROW LEVEL SECURITY这些形式控制属于表的行安全性策略的应用。如果启用且表不存在任何策略,则应用默认拒绝策略。请注意,即使禁用了表的行级安全,表仍然可以存在策略;在这种情况下,这些策略不会被应用,并且会被忽略。另请参见 CREATE POLICY。
NO FORCE/FORCE ROW LEVEL SECURITY这些形式控制当用户是表所有者时属于表的行安全性策略的应用。如果启用,当用户是表所有者时会应用行级安全策略。如果禁用(默认设置),当用户是表所有者时不会应用行级安全。另请参见 CREATE POLICY。
CLUSTER ON该形式为将来的 CLUSTER 操作选择默认索引。它实际上不会对表重新聚簇。
更改聚簇选项会获取一个
SHARE UPDATE EXCLUSIVE锁。SET WITHOUT CLUSTER该形式从表中移除最近使用的 CLUSTER 索引规范。这会影响将来的聚簇操作(这些操作未指定索引)。
更改聚簇选项会获取一个
SHARE UPDATE EXCLUSIVE锁。SET WITH OIDS该形式向表添加一个
oid系统列(参见第 5.4 节)。如果表已有 OID,则不执行任何操作。请注意,这并不等同于
ADD COLUMN oid oid;后者会添加一个碰巧命名为oid的普通列,而不是系统列。SET WITHOUT OIDS该形式从表中移除
oid系统列。这完全等同于DROP COLUMN oid RESTRICT,但如果表中已经没有oid列,则不会报错。SET TABLESPACE该形式将表的表空间更改为指定的表空间,并将与表关联的数据文件移动到新表空间。表上的索引(如果有)不会移动,但可以通过额外的
SET TABLESPACE命令单独移动。可以使用ALL IN TABLESPACE形式移动当前数据库中某个表空间内的所有表;该形式会先锁定所有要移动的表,然后逐个移动。该形式还支持OWNED BY,只移动指定角色所拥有的表。如果指定NOWAIT选项,而命令无法立即获取所需的全部锁,则会失败。请注意,该命令不会移动系统目录;如有需要,请改用ALTER DATABASE或显式调用ALTER TABLE。information_schema关系不被视为系统目录的一部分,因此会被移动。另请参见 CREATE TABLESPACE。SET { LOGGED | UNLOGGED }该形式将表从不记录 WAL 的表更改为记录 WAL 的表,或反之(参见
UNLOGGED)。不能将其应用于临时表。SET (storage_parameter[=value] [, ... ] )该形式更改表的一个或多个存储参数。有关可用参数的详情,请参见存储参数。请注意,该命令不会立即修改表内容;根据参数的不同,可能需要重写表才能达到预期效果。可以使用 VACUUM FULL、CLUSTER,或
ALTER TABLE中会强制重写表的某种形式来完成重写。对于与规划器相关的参数,更改会从下次锁定表时生效,因此不会影响当前正在执行的查询。设置 fillfactor 和 autovacuum 存储参数,以及规划器参数
parallel_workers时,会获取SHARE UPDATE EXCLUSIVE锁。注意
虽然
CREATE TABLE允许在WITH (语法中指定storage_parameter)OIDS,但ALTER TABLE不会将OIDS视为存储参数。应改用SET WITH OIDS和SET WITHOUT OIDS形式来更改 OID 状态。RESET (storage_parameter[, ... ] )该形式把一个或多个存储参数重置为默认值。与
SET一样,可能仍需要进行表重写才能让整张表完全更新。INHERITparent_table该形式将目标表添加为指定父表的新子表。之后,对父表执行的查询将包含目标表的记录。要作为子表添加,目标表必须已经包含与父表相同的所有列(也可以有额外的列)。列必须具有匹配的数据类型;如果父表中的列具有
NOT NULL约束,则子表中的相应列也必须具有NOT NULL约束。父表的所有
CHECK约束还必须在子表中有匹配的约束,但标记为不可继承的约束除外(即在父表中使用ALTER TABLE ... ADD CONSTRAINT ... NO INHERIT创建的约束);这类约束会被忽略。所有匹配的子表约束都不能标记为不可继承。目前不考虑UNIQUE、PRIMARY KEY和FOREIGN KEY约束,但将来可能会改变。NO INHERITparent_table该形式把目标表从指定父表的子表列表中移除。对父表的查询将不再包含来自目标表的记录。
OFtype_name该形式将表关联到一个复合类型,就像由
CREATE TABLE OF创建该表一样。表的列名和类型列表必须与复合类型的完全匹配;是否存在oid系统列可以不同。该表不能继承任何其他表。这些限制确保CREATE TABLE OF允许等效的表定义。NOT OF该形式会解除类型化表与其类型之间的关联。
OWNER该形式把表、序列、视图、物化视图或外部表的所有者更改为指定用户。
REPLICA IDENTITY#该形式修改写入预写式日志的信息,以标识被更新或删除的行。在大多数情况下,只有当某列的旧值与新值不同,才记录该列的旧值;不过,如果旧值是外部存储的,则无论它是否改变,都会被记录。除非正在使用逻辑复制,否则该选项没有效果。
DEFAULT记录主键列(如果有)的旧值。这是非系统表的默认值。
USING INDEXindex_name记录由指定索引覆盖的列的旧值。该索引必须是唯一的、非部分的、不可延迟的,并且只包含被标记为
NOT NULL的列。如果该索引被删除,其行为与NOTHING相同。FULL记录该行中所有列的旧值。
NOTHING不记录关于旧行的任何信息。这是系统表的默认值。
RENAMERENAME形式更改表(或索引、序列、视图、物化视图或外部表)的名称,表中单个列的名称,或表的约束名称。存储的数据不受影响。SET SCHEMA该形式把表移动到另一个模式。相关索引、约束以及由表列拥有的序列也会一并移动。
ATTACH PARTITIONpartition_nameFOR VALUESpartition_bound_spec该形式使用与 CREATE TABLE 中
partition_bound_spec相同的语法,将现有表(该表本身也可以分区)附加为目标表的分区。分区边界规范必须符合目标表的分区策略和分区键。要附加的表必须拥有与目标表完全相同的列,不能多也不能少;此外,列类型也必须匹配。它还必须拥有目标表的所有NOT NULL和CHECK约束。目前不考虑UNIQUE、PRIMARY KEY和FOREIGN KEY约束。如果要附加的表中的任何CHECK约束标记为NO INHERIT,命令将失败;必须在不带NO INHERIT子句的情况下重新创建此类约束。如果新分区是普通表,则会执行完整的表扫描,以检查表中的现有行是否违反分区约束。在运行此命令之前,可以向表添加一个有效的
CHECK约束,使其只允许满足所需分区约束的行,从而避免扫描。系统会使用该CHECK约束来判断无需扫描表即可验证分区约束。但是,如果任何分区键是表达式且分区不接受NULL值,这种方法就不起作用。如果附加不接受NULL值的列表分区,还应向分区键列添加NOT NULL约束,除非分区键是表达式。如果新分区是外部表,则不会采取任何措施来验证外部表中的所有行是否遵守分区约束。(参见 CREATE FOREIGN TABLE 中关于外部表约束的讨论。)
DETACH PARTITIONpartition_name该形式从目标表分离指定的分区。分离后的分区继续作为独立表存在,但不再与分离它的表有任何关联。
所有作用于单个表的 ALTER TABLE 形式,除 RENAME、SET SCHEMA、ATTACH PARTITION 和 DETACH PARTITION 外,都可以组合成一个多项修改列表,一并执行。例如,可以在一条命令中添加多个列并且/或者修改多个列的类型。对于大型表,这一点尤其有用,因为这样只需对表执行一次遍历。
要使用 ALTER TABLE,必须拥有该表。要更改表的模式或表空间,还必须在新模式或表空间上拥有 CREATE 权限。要将表作为父表的新子表添加,还必须拥有父表。要将一个表作为另一个表的新分区附加,还必须拥有要附加的表。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在表所在模式上拥有 CREATE 权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建表完成的操作。不过,超级用户无论如何都可以更改任何表的所有权。)要添加列、更改列类型或使用 OF 子句,还必须在数据类型上拥有 USAGE 权限。
参数
IF EXISTS如果表不存在,则不要抛出错误。这种情况下会发出一条提示。
name要修改的一个现有表的名称(可以是模式限定的)。如果在表名前指定了
ONLY,则只会修改该表。如果没有指定ONLY,该表及其所有后代表(如果有)都会被修改。可选地,在表名后面可以指定*用来显式地指示包括后代表。column_name一个新列或者现有列的名称。
new_column_name一个现有列的新名称。
new_name该表的新名称。
data_type一个新列的数据类型或者一个现有列的新数据类型。
table_constraint该表的新的表约束。
constraint_name一个新约束或者现有约束的名称。
CASCADE自动删除依赖于被删除列或约束的对象(例如引用该列的视图),并且接着删除依赖于那些对象的所有对象(见第 5.13 节)。
RESTRICT如果有任何依赖对象时拒绝删除列或者约束。这是默认行为。
trigger_name要禁用或启用的单个触发器的名称。
ALL禁用或启用属于表的所有触发器。(如果触发器中有内部生成的约束触发器,例如用于实现外键约束或可延迟唯一性约束和排他约束的触发器,则需要超级用户权限。)
USER禁用或启用属于表的所有触发器,但不包括内部生成的约束触发器(例如用于实现外键约束或可延迟的唯一约束和排他约束的触发器)。
index_name一个现有索引的名称。
storage_parameter一个表存储参数的名称。
value一个表存储参数的新值。根据该参数,该值可能是一个数字或者一个词。
parent_table要与这个表关联或者解除关联的父表。
new_owner该表的新所有者的用户名。
new_tablespace要把该表移入其中的表空间的名称。
new_schema要把该表移入其中的模式的名称。
partition_name要附加为该表的新分区或从该表分离出去的表名。
partition_bound_spec新分区的分区边界说明。关于该语法的更多细节,请参见 CREATE TABLE。
注解
关键字 COLUMN 不影响语义,可以省略。
使用 ADD COLUMN 添加列时,表中的所有现有行都会使用该列的默认值初始化(如果未指定 DEFAULT 子句,则使用 NULL)。如果没有 DEFAULT 子句,这只是一次元数据更改,不需要立即更新表数据;添加的 NULL 值会在读取时提供。
使用 DEFAULT 子句添加列,或更改现有列的类型,都需要重写整个表及其索引。作为更改现有列类型时的例外,如果 USING 子句不改变列内容,并且旧类型要么可以二进制强制转换为新类型,要么是新类型之上的无约束域,则不需要重写表;但受影响列上的任何索引仍必须重建。添加或移除系统 oid 列也需要重写整个表。对于大型表,重建表和/或索引可能需要相当长的时间,并且会暂时需要最多两倍的磁盘空间。
添加 CHECK 或 NOT NULL 约束需要扫描表,以验证现有行满足约束,但不需要重写表。
类似地,在附加新分区时,也可能会扫描该分区以验证现有行满足分区约束。
在单个 ALTER TABLE 中允许指定多个更改的主要原因,是可以把多次表扫描或重写合并为对表的一次遍历。
扫描大表以验证新的外键或检查约束可能需要很长时间,并且在 ALTER TABLE ADD CONSTRAINT 命令提交之前,会阻止对该表的其他更新。NOT VALID 约束选项的主要目的,是减小添加约束对并发更新的影响。使用 NOT VALID 时,ADD CONSTRAINT 命令不会扫描表,因此可以立即提交。之后可以发出 VALIDATE CONSTRAINT 命令,以验证现有行满足该约束。验证步骤不需要阻止并发更新,因为它知道其他事务会对它们插入或更新的行强制执行该约束;只需检查预先存在的行。因此,验证只会在被修改的表上获取 SHARE UPDATE EXCLUSIVE 锁。(如果约束是外键,则被该约束引用的表上还需要 ROW SHARE 锁。)除了改善并发性之外,在已知该表包含既有违规数据的情况下,NOT VALID 和 VALIDATE CONSTRAINT 也很有用。一旦约束已经建立,就不能再插入新的违规数据,而现有问题则可以从容修正,直到 VALIDATE CONSTRAINT 最终成功。
DROP COLUMN 形式不会从物理上删除列,而只是使其对 SQL 操作不可见。表后续的插入和更新操作会为该列存储空值。因此,删除列的速度很快,但不会立即减少表在磁盘上的大小,因为被删除列占用的空间不会被回收。随着现有行被更新,空间会逐渐回收。(删除系统 oid 列时不适用这些说明;删除该列会立即重写表。)
若要强制立即回收已删除列所占的空间,可以执行任何一种会导致整表重写的 ALTER TABLE 形式。这样会重建每一行,并用空值替换被删除的列。
会重写表的 ALTER TABLE 形式对于 MVCC 来说并不安全。表重写完成后,如果并发事务使用的是在重写发生之前取得的快照,那么该表在这些并发事务看来会像一张空表。详见第 13.5 节。
SET DATA TYPE 的 USING 选项实际上可以指定任何涉及该行旧值的表达式;也就是说,它既可以引用正在转换的列,也可以引用其他列。这使得使用 SET DATA TYPE 语法完成非常通用的转换成为可能。正因为这种灵活性,USING 表达式不会应用到列的默认值(如果有)上,因为其结果可能不是默认值所要求的常量表达式。这意味着,当从旧类型到新类型不存在隐式或赋值类型转换时,即便提供了 USING 子句,SET DATA TYPE 也可能仍然无法转换默认值。在这种情况下,可以先用 DROP DEFAULT 删除默认值,执行 ALTER TYPE,然后再用 SET DEFAULT 添加一个合适的新默认值。类似的考虑也适用于涉及该列的索引和约束。
如果表有任何后代表,则不允许只在父表中添加、重命名或更改列的类型,而不对后代表执行相同操作。这确保后代表始终拥有与父表匹配的列。同样,不能只在父表中重命名约束,而不在所有后代表中重命名它,以便父表和后代表中的约束也能匹配。此外,由于从父表中选择也会选择其后代表中的数据,除非父表上的约束也在这些后代表上标记为有效,否则不能将该约束标记为有效。在所有这些情况下,ALTER TABLE ONLY 都会被拒绝。
只有当某个后代表中的列既不是从其他父表继承而来,也从未有过该列的独立定义时,递归的 DROP COLUMN 操作才会移除该后代表中的此列。非递归的 DROP COLUMN(即 ALTER TABLE ONLY ... DROP COLUMN)永远不会移除任何后代列,而只会把它们标记为独立定义,而非继承得到。对于分区表,非递归的 DROP COLUMN 命令会失败,因为一张表的所有分区都必须与分区根表拥有相同的列。
标识列的操作(ADD GENERATED、SET 等,以及 DROP IDENTITY),以及 TRIGGER、CLUSTER、OWNER 和 TABLESPACE 操作,都不会递归到后代表;也就是说,它们的行为始终如同指定了 ONLY。添加约束时,只有未标记为 NO INHERIT 的 CHECK 约束会递归。
不允许更改系统目录表的任何部分。
有关有效参数的进一步说明,请参见 CREATE TABLE。第 5 章中还有关于继承的更多信息。
示例
要添加类型为 varchar 的列到表中:
ALTER TABLE distributors ADD COLUMN address varchar(30);
要从表中删除一列:
ALTER TABLE distributors DROP COLUMN address RESTRICT;
要在一个操作中更改两个现有列的类型:
ALTER TABLE distributors
ALTER COLUMN address TYPE varchar(80),
ALTER COLUMN name TYPE varchar(100);
要把一个包含 Unix 时间戳的整数列改为
timestamp with time zone,并通过 USING 子句完成转换:
ALTER TABLE foo
ALTER COLUMN foo_timestamp SET DATA TYPE timestamp with time zone
USING
timestamp with time zone 'epoch' + foo_timestamp * interval '1 second';
如果该列带有一个不能自动转换为新数据类型的默认值表达式,也是同样的做法:
ALTER TABLE foo
ALTER COLUMN foo_timestamp DROP DEFAULT,
ALTER COLUMN foo_timestamp TYPE timestamp with time zone
USING
timestamp with time zone 'epoch' + foo_timestamp * interval '1 second',
ALTER COLUMN foo_timestamp SET DEFAULT now();
要重命名一个现有列:
ALTER TABLE distributors RENAME COLUMN address TO city;
重命名一个现有的表:
ALTER TABLE distributors RENAME TO suppliers;
重命名一个现有的约束:
ALTER TABLE distributors RENAME CONSTRAINT zipchk TO zip_check;
为一列增加一个非空约束:
ALTER TABLE distributors ALTER COLUMN street SET NOT NULL;
从一列移除一个非空约束:
ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL;
要向一个表及其所有后代添加一个检查约束:
ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
要只向一个表本身添加检查约束,而不添加到其后代:
ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5) NO INHERIT;
(该检查约束也不会被未来的后代表继承。)
要从一个表及其所有后代移除一个检查约束:
ALTER TABLE distributors DROP CONSTRAINT zipchk;
只从一个表移除一个检查约束:
ALTER TABLE ONLY distributors DROP CONSTRAINT zipchk;
(该检查约束在所有子表上仍然保留。)
为一个表增加一个外键约束:
ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses (address);
为一个表增加一个外键约束,并且尽量不要影响其他工作:
ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses (address) NOT VALID; ALTER TABLE distributors VALIDATE CONSTRAINT distfk;
为一个表增加一个(多列)唯一约束:
ALTER TABLE distributors ADD CONSTRAINT dist_id_zipcode_key UNIQUE (dist_id, zipcode);
为一个表增加一个自动命名的主键约束,注意一个表只能拥有一个主键:
ALTER TABLE distributors ADD PRIMARY KEY (dist_id);
把一个表移动到一个不同的表空间:
ALTER TABLE distributors SET TABLESPACE fasttablespace;
把一个表移动到一个不同的模式:
ALTER TABLE myschema.distributors SET SCHEMA yourschema;
重建一个主键约束,并且在重建索引期间不阻塞更新:
CREATE UNIQUE INDEX CONCURRENTLY dist_id_temp_idx ON distributors (dist_id);
ALTER TABLE distributors DROP CONSTRAINT distributors_pkey,
ADD CONSTRAINT distributors_pkey PRIMARY KEY USING INDEX dist_id_temp_idx;将分区附加到范围分区表:
ALTER TABLE measurement
ATTACH PARTITION measurement_y2016m07 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');将分区附加到列表分区表:
ALTER TABLE cities
ATTACH PARTITION cities_ab FOR VALUES IN ('a', 'b');从分区表分离分区:
ALTER TABLE measurement
DETACH PARTITION measurement_y2015m12;兼容性
以下形式符合 SQL 标准:ADD(不带 USING INDEX)、DROP [COLUMN]、DROP IDENTITY、RESTART、SET DEFAULT、SET DATA TYPE(不带 USING)、SET GENERATED 以及 SET 。其他形式是 PostgreSQL 对 SQL 标准的扩展。此外,在单条 sequence_optionALTER TABLE 命令中指定多个操作的能力也是一种扩展。
ALTER TABLE DROP COLUMN 可以被用来删除一个表的唯一的列,从而留下一个零列的表。这是一种 SQL 的扩展,SQL 中不允许零列的表。