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_name{ FOR VALUESpartition_bound_spec| DEFAULT } ALTER TABLE [ IF EXISTS ]nameDETACH PARTITIONpartition_name[ CONCURRENTLY | FINALIZE ] whereactionis one of: 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_nameSET EXPRESSION AS (expression) ALTER [ COLUMN ]column_nameDROP EXPRESSION [ IF EXISTS ] 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 STATISTICS {integer| DEFAULT } ALTER [ COLUMN ]column_nameSET (attribute_option=value[, ... ] ) ALTER [ COLUMN ]column_nameRESET (attribute_option[, ... ] ) ALTER [ COLUMN ]column_nameSET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT } ALTER [ COLUMN ]column_nameSET COMPRESSIONcompression_methodADDtable_constraint[ NOT VALID ] ADDtable_constraint_using_indexALTER CONSTRAINTconstraint_name[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ ENFORCED | NOT ENFORCED ] ALTER CONSTRAINTconstraint_name[ INHERIT | NO INHERIT ] 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 WITHOUT OIDS SET ACCESS METHOD {new_access_method| DEFAULT } SET TABLESPACEnew_tablespaceSET { LOGGED | UNLOGGED } SET (storage_parameter[=value] [, ... ] ) RESET (storage_parameter[, ... ] ) INHERITparent_tableNO INHERITparent_tableOFtype_nameNOT OF OWNER TO {new_owner| CURRENT_ROLE | CURRENT_USER | SESSION_USER } REPLICA IDENTITY { DEFAULT | USING INDEXindex_name| FULL | NOTHING } andpartition_bound_specis: IN (partition_bound_expr[, ...] ) | FROM ( {partition_bound_expr| MINVALUE | MAXVALUE } [, ...] ) TO ( {partition_bound_expr| MINVALUE | MAXVALUE } [, ...] ) | WITH ( MODULUSnumeric_literal, REMAINDERnumeric_literal) andcolumn_constraintis: [ CONSTRAINTconstraint_name] { NOT NULL [ NO INHERIT ] | NULL | CHECK (expression) [ NO INHERIT ] | DEFAULTdefault_expr| GENERATED ALWAYS AS (generation_expr) [ STORED | VIRTUAL ] | GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ (sequence_options) ] | UNIQUE [ NULLS [ NOT ] DISTINCT ]index_parameters| PRIMARY KEYindex_parameters| REFERENCESreftable[ (refcolumn) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETEreferential_action] [ ON UPDATEreferential_action] } [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ ENFORCED | NOT ENFORCED ] andtable_constraintis: [ CONSTRAINTconstraint_name] { CHECK (expression) [ NO INHERIT ] | NOT NULLcolumn_name[ NO INHERIT ] | UNIQUE [ NULLS [ NOT ] DISTINCT ] (column_name[, ... ] [,column_nameWITHOUT OVERLAPS ] )index_parameters| PRIMARY KEY (column_name[, ... ] [,column_nameWITHOUT OVERLAPS ] )index_parameters| EXCLUDE [ USINGindex_method] (exclude_elementWITHoperator[, ... ] )index_parameters[ WHERE (predicate) ] | FOREIGN KEY (column_name[, ... ] [, PERIODcolumn_name] ) REFERENCESreftable[ (refcolumn[, ... ] [, PERIODrefcolumn] ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETEreferential_action] [ ON UPDATEreferential_action] } [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ ENFORCED | NOT ENFORCED ] andtable_constraint_using_indexis: [ CONSTRAINTconstraint_name] { UNIQUE | PRIMARY KEY } USING INDEXindex_name[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]index_parametersinUNIQUE,PRIMARY KEY, andEXCLUDEconstraints are: [ INCLUDE (column_name[, ... ] ) ] [ WITH (storage_parameter[=value] [, ... ] ) ] [ USING INDEX TABLESPACEtablespace_name]exclude_elementin anEXCLUDEconstraint is: {column_name| (expression) } [ COLLATEcollation] [opclass[ (opclass_parameter=value[, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]referential_actionin aFOREIGN KEY/REFERENCESconstraint is: { NO ACTION | RESTRICT | CASCADE | SET NULL [ (column_name[, ... ] ) ] | SET DEFAULT [ (column_name[, ... ] ) ] }
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子句。
当使用该形式时,列的统计信息会被删除,因此建议随后在该表上运行 ANALYZE。 对于虚拟生成列,则不需要ANALYZE,因为这类列从不具有统计信息。
SET/DROP DEFAULT #这些形式设置或移除列的默认值(其中移除等同于将默认值设为 NULL)。新的默认值只会应用于后续的INSERT或UPDATE命令;它不会导致表中已有的行发生变化。
SET/DROP NOT NULL #这些形式改变列是被标记为允许空值,还是被标记为拒绝空值。
SET NOT NULL只能应用于满足以下条件的列:表中没有任何记录在该列上包含NULL值。通常,系统会在执行ALTER TABLE时通过扫描整个表来检查这一点,除非指定了NOT VALID;不过,如果已存在一个有效的CHECK约束(并且不会在同一命令中被删除)能够证明不可能存在NULL,则会跳过表扫描。如果一列带有一个无效的非空约束,SET NOT NULL会对其进行验证。
如果该表是一个分区,而该列在父表中被标记为NOT NULL,则不能对该列执行DROP NOT NULL。要从所有分区中删除NOT NULL约束,请在父表上执行DROP NOT NULL。即使父表上没有NOT NULL约束,也仍然可以根据需要在单独的分区上添加此类约束;也就是说,即使父表允许空值,子表仍可禁止空值,但反过来不行。也可以仅从父表上执行DROP NOT NULL(即配合ONLY),这样不会从子表中移除该约束。
SET EXPRESSION AS #该形式会替换生成列的表达式。存储生成列中的现有数据会被重写,此后的所有更改都会应用新的生成表达式。
当该形式用于存储生成列时,其统计信息会被删除,因此建议随后在该表上运行 ANALYZE。 对于虚拟生成列,则不需要ANALYZE,因为这类列从不具有统计信息。
DROP EXPRESSION [ IF EXISTS ] #该形式会把一个存储生成列转换成普通基列。列中的现有数据会被保留,但今后的更改将不再应用生成表达式。
目前该形式只支持存储生成列(不支持虚拟生成列)。
如果指定了DROP EXPRESSION IF EXISTS,而该列不是生成列,则不会报错;此时会发出一条提示。
ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITYSET GENERATED { ALWAYS | BY DEFAULT }DROP IDENTITY [ IF EXISTS ] #这些形式改变列是否为标识列,或者更改现有标识列的生成属性。详情见CREATE TABLE。与SET DEFAULT类似,这些形式只影响后续INSERT和UPDATE命令的行为;它们不会导致表中已有的行发生变化。
如果指定了DROP IDENTITY IF EXISTS,而该列不是标识列,则不会报错;此时会发出一条提示。
SET sequence_optionRESTART #这些形式修改现有标识列底层的序列。sequence_option是ALTER SEQUENCE支持的一个选项,例如INCREMENT BY。
SET STATISTICS #该形式为后续ANALYZE操作设置列级统计信息收集目标。该目标可以设置在 0 到 10000 之间。将其设为DEFAULT可以恢复使用系统默认的统计目标(default_statistics_target)。(设为 -1 也是一种已废弃的写法,效果相同。)有关PostgreSQL查询规划器如何使用统计信息的更多信息,请参见Section 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影响为该表及其继承子表收集的统计信息,以及为分区表收集的统计信息。当指定值为正数时,查询规划器将假定该列恰好包含指定数量的不同非空值。也可以通过使用小于 0 且大于等于 -1 的值来指定小数值。这会指示查询规划器通过将指定数字的绝对值乘以表中估计行数来估算不同值的数量。例如,值为 -1 表示该列中的所有值都不同,而值为 -0.5 表示每个值平均出现两次。当表的大小随时间变化时,这会很有用。有关PostgreSQL查询规划器如何使用统计信息的更多信息,请参见Section 14.2。
更改每个属性的选项会获取一个SHARE UPDATE EXCLUSIVE锁。
SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT } #该形式设置列的存储模式。这控制该列的数据是内联保存还是保存在辅助TOAST表中,以及数据是否应被压缩。PLAIN必须用于诸如integer之类的固定长度值,表示内联且不压缩。MAIN用于内联、可压缩的数据。EXTERNAL用于外部、不压缩的数据,而EXTENDED用于外部、已压缩的数据。写成DEFAULT会把存储模式设置为该列数据类型的默认模式。对于大多数支持非PLAIN存储的数据类型,默认模式是EXTENDED。使用EXTERNAL会让对非常大的text和bytea值执行子串操作更快,但代价是占用更多存储空间。请注意,ALTER TABLE ... SET STORAGE本身不会改变表中的任何内容;它只是设定未来更新表时要采用的策略。更多信息见Section 63.2。
SET COMPRESSION compression_method #该形式设置列的压缩方法,用来决定未来插入的值将如何被压缩(前提是存储模式允许压缩)。这不会导致表被重写,因此现有数据仍可能采用其他压缩方法。如果使用pg_restore恢复该表,那么所有值都会按配置的压缩方法重新写入。然而,当数据是从另一个关系插入时(例如通过INSERT ... SELECT),源表中的值不一定会被去 TOAST 化,因此先前已压缩的数据可能会保留原有的压缩方法,而不是按目标列的压缩方法重新压缩。支持的压缩方法有pglz和lz4。(lz4仅在使用--with-lz4构建PostgreSQL时才可用。)此外,compression_method还可以是default,表示在数据插入时查询default_toast_compression设置,以决定要使用的方法。
ADD table_constraint [ NOT VALID ] #该形式使用与CREATE TABLE相同的约束语法向表中添加一个新约束,另外还支持选项NOT VALID;目前该选项只允许用于外键、CHECK以及非空约束。
通常,该形式会扫描表,以验证表中所有现有行都满足新约束。但如果使用了NOT VALID选项,就会跳过这一步可能非常耗时的扫描。该约束仍会对后续的插入或更新生效(也就是说,对于外键,除非被引用表中存在匹配行,否则操作会失败;对于检查约束,除非新行满足指定的检查条件,否则操作会失败)。但是,在通过VALIDATE CONSTRAINT选项完成验证之前,数据库不会假定该约束对表中的所有行都成立。请参见下面的Notes,了解使用NOT VALID选项的更多信息。
尽管大多数形式的ADD 需要table_constraintACCESS EXCLUSIVE锁,ADD FOREIGN KEY只需要SHARE ROW EXCLUSIVE锁。请注意,ADD FOREIGN KEY除了在声明约束的表上获取锁之外,还会在被引用的表上获取SHARE ROW EXCLUSIVE锁。
当向分区表添加唯一约束或主键约束时,还会受到附加限制;请参见CREATE TABLE。
ADD table_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 UNIQUE INDEX CONCURRENTLY创建索引,然后再用此语法把它转换成约束。参见下面的示例。
ALTER CONSTRAINT #该形式修改一个先前创建的约束的属性。目前只有外键约束可以用这种方式修改,但见下文。
ALTER CONSTRAINT ... INHERITALTER CONSTRAINT ... NO INHERIT #这些形式会修改一个可继承约束,使其变为不可继承,或者反过来。当前只有非空约束可以这样修改。除了改变该约束的可继承状态之外,在把一个不可继承约束标记为可继承、且该表存在子表时,系统还会向这些子表添加一个等价约束。如果在存在子表的表上把一个可继承约束标记为不可继承,则子表上的对应约束会被标记为不再继承,但不会被移除。
VALIDATE CONSTRAINT #该形式通过扫描表来验证一个先前以NOT VALID创建的外键、检查或非空约束,以确保不存在任何不满足该约束的行。如果该约束被设置为NOT ENFORCED,则会引发错误。如果该约束已经被标记为有效,则不会发生任何事情。(关于此命令有何用途,请参见下面的Notes。)
此命令会获取一个SHARE UPDATE EXCLUSIVE锁。
DROP CONSTRAINT [ IF EXISTS ] #该形式删除表上指定的约束,以及支撑该约束的任何索引。如果指定了IF EXISTS而该约束不存在,则不会报错;此时会发出一条提示。
DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER #这些形式配置表上触发器的触发行为。被禁用的触发器仍为系统所知,但在其触发事件发生时不会执行。(对于延迟触发器,启用状态是在事件发生时检查,而不是在触发器函数实际执行时检查。)可以禁用或启用按名称指定的单个触发器、表上的全部触发器,或者仅用户触发器(此选项排除内部生成的约束触发器,例如用于实现外键约束或可延迟的唯一约束和排他约束的触发器)。禁用或启用内部生成的约束触发器需要超级用户权限;应谨慎执行,因为如果这些触发器不执行,就无法保证约束的完整性。
触发器的触发机制也受配置变量session_replication_role的影响。普通启用的触发器(默认)会在复制角色为“origin”(默认)或“local”时触发。配置为ENABLE REPLICA的触发器仅会在会话处于“replica”模式时触发,而配置为ENABLE ALWAYS的触发器无论当前复制角色为何都会触发。
这种机制的效果是,在默认配置下,触发器不会在副本上触发。这很有用,因为如果在源端使用触发器在表之间传播数据,那么复制系统也会复制这些传播后的数据;因此触发器不应在副本上再次触发,否则会导致重复。不过,如果触发器用于其他目的,例如生成外部告警,那么把它设置为ENABLE ALWAYS、使其在副本上也触发,可能就是合适的做法。
当该命令应用于分区表时,分区中对应的克隆触发器的状态也会被更新,除非指定了ONLY。
此命令会获取一个SHARE ROW EXCLUSIVE锁。
DISABLE/ENABLE [ REPLICA | ALWAYS ] RULE #这些形式配置表所属重写规则是否应用。 禁用的规则仍然为系统所知,但在查询重写期间不会应用。 语义与禁用/启用触发器相同。对于ON SELECT规则,此配置将被忽略, 这些规则始终会被应用,以确保视图在当前会话处于非默认复制角色时仍然正常工作。
规则触发机制也受配置变量session_replication_role的影响,类似于上面描述的触发器。
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 WITHOUT OIDS #这是用于移除oid系统列的向后兼容语法。由于如今已无法再添加oid系统列,因此它实际上永远不会产生任何效果。
SET ACCESS METHOD #该形式通过使用指定的访问方法重写表来改变其访问方法;指定DEFAULT则会选择由配置参数default_table_access_method设定的访问方法。更多信息请参见Chapter 61。
当应用于分区表时,没有数据需要重写,但之后创建的分区默认会使用给定的访问方法,除非被USING子句覆盖。指定DEFAULT会移除先前设置的值,使未来的分区重新默认使用default_table_access_method。
SET TABLESPACE #该形式把表的表空间更改为指定的表空间,并将与该表关联的数据文件移动到新的表空间。表上的索引(如果有)不会被移动,但可以通过额外的SET TABLESPACE命令单独移动。当应用于分区表时,不会移动任何内容,但之后通过CREATE TABLE PARTITION OF创建的分区会使用该表空间,除非被TABLESPACE子句覆盖。
使用ALL IN TABLESPACE形式可以移动当前数据库中位于某个表空间中的所有表;该形式会先锁定所有待移动表,然后逐个移动。该形式还支持OWNED BY,从而只移动指定角色拥有的表。如果指定了NOWAIT,则一旦无法立即获取所需的全部锁,命令就会失败。请注意,系统目录不会通过此命令移动;如果需要,可改用ALTER DATABASE或显式的ALTER TABLE调用。information_schema中的关系不被视为系统目录的一部分,因此会被移动。另请参阅CREATE TABLESPACE。
SET { LOGGED | UNLOGGED } #该形式把表从未记录日志改为记录日志,或反过来(参见UNLOGGED)。它不能应用于临时表。
这也会改变与该表关联的任何序列(用于标识列或 serial 列)的持久性。不过,也可以单独更改这些序列的持久性。
该形式不支持分区表。
SET ( storage_parameter [= value] [, ... ] ) #该形式更改表的一个或多个存储参数。可用参数的详细信息见Storage Parameters中的CREATE TABLE文档。请注意,此命令不会立即修改表内容;根据参数不同,你可能需要重写表才能获得预期效果。这可以通过VACUUM FULL、CLUSTER,或者任何会强制重写表的ALTER TABLE形式来完成。对于与规划器相关的参数,更改会从下一次锁定该表时起生效,因此当前正在执行的查询不会受到影响。
对于 fillfactor、toast 和 autovacuum 存储参数,以及规划器参数parallel_workers,会获取SHARE UPDATE EXCLUSIVE锁。
RESET ( storage_parameter [, ... ] ) #该形式把一个或多个存储参数重置为默认值。与SET一样,可能仍需要进行表重写才能让整张表完全更新。
INHERIT parent_table #该形式把目标表添加为指定父表的新子表。此后,对父表的查询将包含目标表中的记录。要被添加为子表,目标表必须已经包含父表的所有同名列(也可以再有额外列)。这些列的数据类型必须匹配。
此外,父表上的所有CHECK和NOT NULL约束也都必须存在于子表上,但被标记为不可继承的约束(即使用ALTER TABLE ... ADD CONSTRAINT ... NO INHERIT创建的约束)除外,这些约束会被忽略。所有与之匹配的子表约束都不能被标记为不可继承。目前不会考虑UNIQUE、PRIMARY KEY和FOREIGN KEY约束,但将来这可能会改变。
NO INHERIT parent_table #该形式把目标表从指定父表的子表列表中移除。对父表的查询将不再包含来自目标表的记录。
OF type_name #该形式把表关联到一个复合类型,就好像它是通过CREATE TABLE OF创建的一样。表的列名和类型列表必须与该复合类型完全一致。该表还必须不继承自任何其他表。这些限制保证了CREATE TABLE OF会允许一个等价的表定义。
NOT OF #该形式会解除类型化表与其类型之间的关联。
OWNER TO #该形式把表、序列、视图、物化视图或外部表的所有者更改为指定用户。
REPLICA IDENTITY #该形式修改写入预写式日志的信息,以标识被更新或删除的行。在大多数情况下,只有当某列的旧值与新值不同,才记录该列的旧值;不过,如果旧值是外部存储的,则无论它是否改变,都会被记录。除非正在使用逻辑复制,否则该选项没有效果。
RENAME #RENAME形式用于更改表(或索引、序列、视图、物化视图或外部表)的名称、表中某个列的名称,或者表上某个约束的名称。重命名一个带有底层索引的约束时,该索引也会一并重命名。已存储的数据不会受到影响。
SET SCHEMA #该形式把表移动到另一个模式。相关索引、约束以及由表列拥有的序列也会一并移动。
ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT } #该形式把一个现有表(它本身也可能已分区)附加为目标表的一个分区。可以使用FOR VALUES把它附加为特定值范围的分区,也可以使用DEFAULT把它附加为默认分区。对于目标表中的每个索引,都会在被附加表中创建一个对应索引;或者,如果已经存在等价索引,则会把它附加到目标表的索引上,就像执行了ALTER INDEX ATTACH PARTITION一样。请注意,如果现有表是外部表,而目标表上存在UNIQUE索引,则当前不允许把该表附加为目标表的分区。(另见CREATE FOREIGN TABLE。)对于目标表中存在的每个用户定义行级触发器,也都会在被附加表中创建一个对应触发器。
使用FOR VALUES的分区,其partition_bound_spec语法与CREATE TABLE中的相同。分区边界说明必须与目标表的分区策略和分区键相对应。待附加的表必须拥有与目标表完全相同且不多不少的列;此外,列类型也必须匹配。它还必须具有目标表上所有未标记为NO INHERIT的NOT NULL和CHECK约束。目前不会考虑FOREIGN KEY约束。如果父表中的UNIQUE和PRIMARY KEY约束尚不存在于该分区中,则会在该分区上创建这些约束。
如果新分区是普通表,则会执行一次全表扫描,以检查表中现有行不会违反分区约束。可以在执行此命令之前,先向该表添加一个有效的CHECK约束,使其只允许满足所需分区约束的行,从而避免这次扫描。系统会利用该CHECK约束来判断无须扫描该表以验证分区约束。不过,如果分区键中有任何一个是表达式,而该分区又不接受NULL值,则这一方法无效。如果附加的是一个不接受NULL值的列表分区,还应向分区键列添加NOT NULL约束,除非该分区键是表达式。
如果新分区是外部表,则不会执行任何检查来验证该外部表中的所有行都满足分区约束。(关于外部表上的约束,请参见CREATE FOREIGN TABLE中的讨论。)
当某个表具有默认分区时,定义一个新分区会改变默认分区的分区约束。默认分区不能包含任何本应移动到新分区中的行,因此系统会扫描默认分区以确认不存在此类行。和扫描新分区一样,如果存在适当的CHECK约束,也可以避免这次扫描;同样地,当默认分区是外部表时,这次扫描总会被跳过。
附加分区会在父表上获取一个SHARE UPDATE EXCLUSIVE锁,此外还会在被附加的表以及默认分区(如果有)上获取ACCESS EXCLUSIVE锁。
如果被附加的表本身是分区表,则还必须在其所有子分区上持有进一步的锁;如果默认分区本身也是分区表,也是如此。添加CHECK约束(如Section 5.12.2.2所述)可以避免对子分区加锁。
DETACH PARTITION partition_name [ CONCURRENTLY | FINALIZE ] #该形式会把目标表的指定分区分离出来。被分离的分区会继续作为一张独立表存在,但不再与原先所在的表保持任何关联。任何附加到目标表索引上的索引都会被分离。任何作为目标表中触发器克隆而创建的触发器都会被移除。对于任何在外键约束中引用该父分区表的表,都会获取SHARE锁。
如果指定了CONCURRENTLY,它将使用较低的锁级别运行,以避免阻塞其他可能正在访问该分区表的会话。在这种模式下,内部会使用两个事务。第一个事务中,会在父表和该分区上都获取SHARE UPDATE EXCLUSIVE锁,并把该分区标记为正在分离;随后提交事务,并等待所有其他正在使用该分区表的事务结束。在所有这些事务完成后,第二个事务会在分区表上获取SHARE UPDATE EXCLUSIVE锁、在分区上获取ACCESS EXCLUSIVE锁,并完成分离过程。系统还会向该分区添加一个重复分区约束的CHECK约束。CONCURRENTLY不能在事务块中运行;如果该分区表包含默认分区,也不允许使用它。
如果指定了FINALIZE,则会完成此前被取消或中断的DETACH CONCURRENTLY调用。在任意时刻,一个分区表中最多只能有一个分区处于等待分离完成的状态。
所有作用于单个表的ALTER TABLE形式,除RENAME、SET SCHEMA、ATTACH PARTITION和DETACH PARTITION外,都可以组合成一个多项修改列表,一并执行。例如,可以在一条命令中添加多个列并且/或者修改多个列的类型。对于大型表,这一点尤其有用,因为这样只需对表执行一次遍历。
要使用ALTER TABLE,你必须拥有该表。要更改表的模式或表空间,你还必须在新模式或新表空间上拥有CREATE权限。要把一个表作为父表的新子表加入,你还必须拥有该父表。此外,要把一个表附加为另一个表的新分区,你必须拥有被附加的表。要更改所有者,你必须能够对新的拥有角色执行SET ROLE,并且该角色必须在该表所在模式上具有CREATE权限。(这些限制确保更改所有者不会做出任何你不能通过删除并重建该表来完成的事情。不过,超级用户无论如何都可以更改任何表的所有权。)要添加列、修改列类型或使用OF子句,你还必须在相应数据类型上具有USAGE权限。
IF EXISTS #如果表不存在,则不要抛出错误。这种情况下会发出一条提示。
name #要修改的一个现有表的名称(可以是模式限定的)。如果在表名前指定了 ONLY,则只会修改该表。如果没有指定ONLY, 该表及其所有后代表(如果有)都会被修改。可选地,在表名后面可以指定 *用来显式地指示包括后代表。
column_name #一个新列或者现有列的名称。
new_column_name #一个现有列的新名称。
new_name #该表的新名称。
data_type #一个新列的数据类型或者一个现有列的新数据类型。
table_constraint #该表的新的表约束。
constraint_name #一个新约束或者现有约束的名称。
CASCADE #自动删除依赖于被删除列或约束的对象(例如引用该列的视图), 并且接着删除依赖于那些对象的 所有对象(见Section 5.15)。
RESTRICT #如果有任何依赖对象时拒绝删除列或者约束。这是默认行为。
trigger_name #一个要禁用或启用的触发器的名称。
ALL #禁用或启用属于表的所有触发器。 (如果任何触发器是内部生成的约束触发器,例如用于实现外键约束或可延迟唯一约束和排他约束的触发器,则需要超级用户权限。)
USER #禁用或启用表中除了内部生成的约束触发器之外的所有触发器,例如用于实现外键约束或可延迟唯一约束和排他约束的触发器。
index_name #一个现有索引的名称。
storage_parameter #一个表存储参数的名称。
value #一个表存储参数的新值。根据该参数,该值可能是一个数字或者一个词。
parent_table #要与这个表关联或者解除关联的父表。
new_owner #该表的新拥有者的用户名。
new_access_method #要将表转换到的访问方法名称。
new_tablespace #要把该表移入其中的表空间的名称。
new_schema #要把该表移入其中的模式的名称。
partition_name #要附加为该表的新分区或从该表分离出去的表名。
partition_bound_spec #新分区的分区边界说明。关于该语法的更多细节,请参见CREATE TABLE。
关键字COLUMN只是噪声,可以省略。
当使用ADD COLUMN添加一列并指定了一个非易失性DEFAULT时,默认值会在该语句执行时计算,其结果会保存在表的元数据中;访问任何现有行时都会返回该值。只有在表被重写时,这个值才会真正应用到行中,因此即使面对大表,ALTER TABLE也会非常快。如果没有指定列约束,则会使用 NULL 作为DEFAULT。在这两种情况下都不需要重写表。
添加带有易失性DEFAULT(例如clock_timestamp())的列、存储生成列、标识列,或者数据类型为带约束域的列,都会导致整个表及其索引被重写。添加虚拟生成列则永远不需要重写。
更改现有列的类型通常会导致整个表及其索引被重写。作为例外,如果更改现有列的类型时,USING子句并未改变列内容,且旧类型要么可以二进制强制转换为新类型,要么是基于新类型的无约束域,则不需要重写表。不过,除非系统能够验证新索引在逻辑上与现有索引等价,否则索引仍会被重建。例如,如果更改了列的排序规则,就必须重建索引,因为新的排序顺序可能不同。然而,在没有改变排序规则的情况下,可以在不重建索引的前提下将列从text改为varchar(反之亦然),因为这两种数据类型的排序方式相同。
对于大表,表和/或索引重建可能会耗费大量时间,并且临时需要多达两倍的磁盘空间。
添加CHECK或NOT NULL约束需要扫描表,以验证现有行满足该约束,但不需要重写表。如果把CHECK约束添加为NOT ENFORCED,则不会执行此验证。
类似地,在附加新分区时,也可能会扫描该分区以验证现有行满足分区约束。
在单个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 操作不可见。此后,对该表的插入和更新操作会为该列存储一个空值。因此,删除一列虽然很快,但不会立刻减少表占用的磁盘空间,因为被删除列所占用的空间尚未被回收。随着现有行被更新,这些空间会逐渐被回收。
若要强制立即回收已删除列所占的空间,可以执行任何一种会导致整表重写的ALTER TABLE形式。这样会重新构造每一行,并用空值替换被删除的列。
会重写表的ALTER TABLE形式对于 MVCC 来说并不安全。表重写完成后,如果并发事务使用的是在重写发生之前取得的快照,那么该表在这些并发事务看来会像一张空表。详见Section 13.6。
SET DATA TYPE的USING选项实际上可以指定任何涉及该行旧值的表达式;也就是说,它既可以引用正在转换的列,也可以引用其他列。这使得使用SET DATA TYPE语法完成非常通用的转换成为可能。正因为这种灵活性,USING表达式不会应用到列的默认值(如果有)上,因为其结果可能不是默认值所要求的常量表达式。这意味着,当从旧类型到新类型不存在隐式或赋值类型转换时,即便提供了USING子句,SET DATA TYPE也可能仍然无法转换默认值。在这种情况下,可以先用DROP DEFAULT删除默认值,执行ALTER TYPE,然后再用SET DEFAULT添加一个合适的新默认值。类似的考虑也适用于涉及该列的索引和约束。
如果某个表有任何后代表,那么在不对后代表执行相同操作的情况下,就不允许在父表中添加列、重命名列或更改列类型。这保证了后代表始终拥有与父表相匹配的列。类似地,如果不同时重命名所有后代上的CHECK约束,就不能只在父表上重命名该检查约束,这样CHECK约束才能在父表及其后代之间保持匹配。(不过,这一限制不适用于基于索引的约束。)此外,由于查询父表时也会同时查询其后代,父表上的约束除非在这些后代上也被标记为有效,否则就不能被标记为有效。在所有这些情况下,ALTER TABLE ONLY都会被拒绝。
只有当某个后代表中的列既不是从其他父表继承而来,也从未有过该列的独立定义时,递归的DROP COLUMN操作才会移除该后代表中的此列。非递归的DROP COLUMN(即ALTER TABLE ONLY ... DROP COLUMN)永远不会移除任何后代列,而只会把它们标记为独立定义,而非继承得到。对于分区表,非递归的DROP COLUMN命令会失败,因为一张表的所有分区都必须与分区根表拥有相同的列。
对于标识列的操作(ADD GENERATED、SET 等、DROP IDENTITY),以及CLUSTER、OWNER和TABLESPACE等操作,永远不会递归到后代表;也就是说,它们总是像指定了ONLY一样执行。影响触发器状态的操作会递归到分区表的各个分区(除非指定了ONLY),但永远不会递归到传统继承体系中的后代表。添加约束时,只有未标记为NO INHERIT的CHECK约束才会递归下去。
不允许更改系统目录表的任何部分。
有关有效参数的进一步说明,请参见CREATE TABLE。Chapter 5中还有关于继承的更多信息。
要向表中添加一个varchar类型的列:
ALTER TABLE distributors ADD COLUMN address varchar(30);
这将导致表中所有现有行都用新列的空值填充。
要添加一个带非空默认值的列:
ALTER TABLE measurements ADD COLUMN mtime timestamp with time zone DEFAULT now();
现有行会以当前时间作为新列的值填充,之后新行会接收其插入时的时间。
要添加一列,并先用与之后默认值不同的值填充它:
ALTER TABLE transactions ADD COLUMN status varchar(30) DEFAULT 'old', ALTER COLUMN status SET default 'current';
现有行会填入old,而后续命令的默认值将是current。 其效果与在两条单独的ALTER TABLE命令中发出这两个子命令相同。
要从表中删除一列:
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 orders
ATTACH PARTITION orders_p4 FOR VALUES WITH (MODULUS 4, REMAINDER 3);
要把默认分区附加到分区表上:
ALTER TABLE cities
ATTACH PARTITION cities_partdef DEFAULT;
从一个分区表分离一个分区:
ALTER TABLE measurement
DETACH PARTITION measurement_y2015m12;
形式ADD [COLUMN]、 DROP [COLUMN]、DROP IDENTITY、RESTART、 SET DEFAULT、SET DATA TYPE(不带USING)、 SET GENERATED以及SET 符合 SQL 标准。形式sequence_optionADD 在省略table_constraintUSING INDEX和NOT VALID子句,且约束类型为 CHECK、UNIQUE、PRIMARY KEY 或REFERENCES之一时,也符合 SQL 标准。其他形式都是 PostgreSQL对 SQL 标准的扩展。此外,在单个 ALTER TABLE命令中指定多个修改也是一种扩展。
ALTER TABLE DROP COLUMN可以被用来删除一个表的唯一的 列,从而留下一个零列的表。这是一种 SQL 的扩展,SQL 中不允许零列的表。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。