ALTER TABLE

ALTER TABLE用于更改一个现有表的定义。

简介

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这些表单设置或删除列的默认值(其中删除等同于将默认值设置为 NULL)。 新的默认值仅适用于后续的INSERTUPDATE命令; 它不会导致表中已有的行发生更改。

SET/DROP NOT NULL这些形式更改一列是否被标记为允许空值或者拒绝空值。

SET NOT NULL 只能应用于列,前提是表中没有任何记录包含该列的NULL值。 通常,这一点在ALTER TABLE全表扫描时来检查;但是,如果找到有效的CHECK约束证明不存在NULL,则跳过表扫描。

如果这个表是一个分区,对于在父表中被标记为NOT NULL的列,不能在其上执行DROP NOT NULL。要从所有的分区中删除NOT NULL约束,可以在父表上执行DROP NOT NULL。即使在父表上没有NOT NULL约束,这样的约束还是能被增加到分区上。也就是说,即便父表允许空值,子表也可以不允许空值,但反过来不行。

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一样,这些形式仅影响随后的INSERTUPDATE命令的行为。 它们不会导致表中已有的行发生更改。

如果DROP IDENTITY IF EXISTS被指定并且该列不是一个标识列,则不会有错误被抛出。在这种情况下会发出一个提示。

SET sequence_optionRESTART这些形式修改位于一个现有标识列之下的序列。 sequence_option是一个 ALTER SEQUENCE 所支持的选项,例如INCREMENT BY

SET STATISTICS这种形式为后续的 ANALYZE 操作设置针对每列的统计收集目标。目标可以被设置在范围 0 到 10000 之间,还可以把它设置为 -1 来恢复到使用系统默认的统计目标(default_statistics_target)。

SET STATISTICS要求一个SHARE UPDATE EXCLUSIVE锁。

SET ( attribute_option = value [, ... ] )RESET ( attribute_option [, ... ] )这种形式设置或者重置每个属性的选项。当前,已定义的针对每个属性的选项只有n_distinctn_distinct_inherited, 它们会覆盖后续 ANALYZE 操作所得到的可区分值数量估计。n_distinct影响该表本身的统计信息,而 n_distinct_inherited影响为该表外加其继承子女收集的统计信息。 当被设置为一个正值时,ANALYZE将假定该列刚好包含指定数量的可区分非空值。当被设置为一个负值(必须大于等于 -1)时, ANALYZE将假定可区分非空值的数量与表的尺寸成线性比例, 确切的计数由估计的表尺寸乘以给定数字的绝对值计算得到。例如,值 -1 表示该列中所有的值都是可区分的,而值 -0.5 则表示每一个值平均出现两次。当表的尺寸随时间变化时,这会有所帮助,因为这种计算只有在查询规划时才会被执行。指定值为 0 将回到正常的估计可区分值数量的做法。

更改针对每个属性的选项要求一个 SHARE UPDATE EXCLUSIVE锁。

SET STORAGE 这种形式为一列设置存储模式。这会控制这列是会被保持在线内还是放在一个二级 TOAST 表中,以及数据是否应被压缩。对于 integer之类的定长、线内、未压缩值必须使用 PLAINMAIN用于线内、可压缩的数据。EXTERNAL用于外部的、未压缩数据。而 EXTENDED用于外部的、压缩数据。对于大部分支持非-PLAIN存储的数据类型,EXTENDED 是默认值。使用EXTERNAL将会让很大的 textbytea之上的子串操作运行得更快, 但是代价是存储空间会增加。注意SET STORAGE本身并不改变表中的任何东西,它只是设置在未来的表更新时要追求的策略。

ADD table_constraint [ NOT VALID ]这种形式使用和 CREATE TABLE 相同的约束语法外加 NOT VALID选项为一个表增加一个新的约束,该选项当前只被允许用于外键和 CHECK 约束。

通常,此窗体将导致对表进行扫描,以验证表中的所有现有行是否满足新约束。 但是如果使用了 NOT VALID选项 ,则跳过此可能很漫长的扫描。 该约束仍将被强制到后续的插入和删除上(也就是说,在外键的情况下如果在被引用表中没有一个匹配的行,操作会失败;或者如果新行不匹配指定的检查条件,操作也会失败)。 但是数据库不会假定约束对该表中的所有行都成立,直到通过使用VALIDATE CONSTRAINT选项对它进行验证。

尽管大多数形式的ADD table_constraint 都需要ACCESS EXCLUSIVE锁,但ADD FOREIGN KEY仅需要SHARE ROW EXCLUSIVE锁。 请注意,除了声明约束的表上的锁之外,ADD FOREIGN KEY还获得了对引用表的SHARE ROW EXCLUSIVE锁。

当唯一或者主键约束被添加到分区表时,会有额外的限制,请参考 CREATE TABLE。 此外,当前分区表上的外键约束不能被声明为NOT VALID

ADD table_constraint_using_index这种形式基于一个已有的唯一索引为一个表增加新的 PRIMARY KEYUNIQUE约束。该索引中的所有列将被包括在约束中。

该索引不能有表达式列或者是一个部分索引。还有,它必须是一个带有默认排序顺序的 B-树索引。这些限制确保该索引等效于使用常规 ADD PRIMARY KEY或者ADD UNIQUE命令时创建的索引。

如果PRIMARY KEY被指定,并且该索引的列没有被标记 NOT NULL,那么这个命令将尝试对每一个这样的列做 ALTER COLUMN SET NOT NULL。这需要一次全表扫描来验证这些列不包含空值。在所有其他情况中,这都是一种很快的操作。

如果提供了一个约束名,那么该索引将被重命名以匹配该约束名。否则该约束将被命名成索引的名称。

这个命令被执行后,该索引被增加的约束“拥有”,这和用常规 ADD PRIMARY KEYADD UNIQUE命令创建的索引一样。特别地,删掉该约束将会导致该索引也消失。

当前在分区表上不支持这种形式。

语法

    ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
        action [, ... ]
    ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
        RENAME [ COLUMN ] column_name TO new_column_name
    ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
        RENAME CONSTRAINT constraint_name TO new_constraint_name
    ALTER TABLE [ IF EXISTS ] name
        RENAME TO new_name
    ALTER TABLE [ IF EXISTS ] name
        SET SCHEMA new_schema
    ALTER TABLE ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ]
        SET TABLESPACE new_tablespace [ NOWAIT ]
    ALTER TABLE [ IF EXISTS ] name
        ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT }
    ALTER TABLE [ IF EXISTS ] name
        DETACH PARTITION partition_name

    其中action 是以下之一:

        ADD [ COLUMN ] [ IF NOT EXISTS ] column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
        DROP [ COLUMN ] [ IF EXISTS ] column_name [ RESTRICT | CASCADE ]
        ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ COLLATE collation ] [ USING expression ]
        ALTER [ COLUMN ] column_name SET DEFAULT expression
        ALTER [ COLUMN ] column_name DROP DEFAULT
        ALTER [ COLUMN ] column_name { SET | DROP } NOT NULL
        ALTER [ COLUMN ] column_name DROP EXPRESSION [ IF EXISTS ]
        ALTER [ COLUMN ] column_name ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ]
        ALTER [ COLUMN ] column_name { SET GENERATED { ALWAYS | BY DEFAULT } | SET sequence_option | RESTART [ [ WITH ] restart ] } [...]
        ALTER [ COLUMN ] column_name DROP IDENTITY [ IF EXISTS ]
        ALTER [ COLUMN ] column_name SET STATISTICS integer
        ALTER [ COLUMN ] column_name SET ( attribute_option = value [, ... ] )
        ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] )
        ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
        ADD table_constraint [ NOT VALID ]
        ADD table_constraint_using_index
        ALTER CONSTRAINT constraint_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
        VALIDATE CONSTRAINT constraint_name
        DROP CONSTRAINT [ IF EXISTS ]  constraint_name [ RESTRICT | CASCADE ]
        DISABLE TRIGGER [ trigger_name | ALL | USER ]
        ENABLE TRIGGER [ trigger_name | ALL | USER ]
        ENABLE REPLICA TRIGGER trigger_name
        ENABLE ALWAYS TRIGGER trigger_name
        DISABLE RULE rewrite_rule_name
        ENABLE RULE rewrite_rule_name
        ENABLE REPLICA RULE rewrite_rule_name
        ENABLE ALWAYS RULE rewrite_rule_name
        DISABLE ROW LEVEL SECURITY
        ENABLE ROW LEVEL SECURITY
        FORCE ROW LEVEL SECURITY
        NO FORCE ROW LEVEL SECURITY
        CLUSTER ON index_name
        SET WITHOUT CLUSTER
        SET WITHOUT OIDS
        SET TABLESPACE new_tablespace
        SET { LOGGED | UNLOGGED }
        SET ( storage_parameter [= value] [, ... ] )
        RESET ( storage_parameter [, ... ] )
        INHERIT parent_table
        NO INHERIT parent_table
        OF type_name
        NOT OF
        OWNER TO { new_owner | CURRENT_USER | SESSION_USER }
        REPLICA IDENTITY { DEFAULT | USING INDEX index_name | FULL | NOTHING }

    and partition_bound_spec is:

    IN ( partition_bound_expr [, ...] ) |
    FROM ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] )
      TO ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] ) |
    WITH ( MODULUS numeric_literal, REMAINDER numeric_literal )

    and column_constraint is:

    [ CONSTRAINT constraint_name ]
    { NOT NULL |
      NULL |
      CHECK ( expression ) [ NO INHERIT ] |
      DEFAULT default_expr |
      GENERATED ALWAYS AS ( generation_expr ) STORED |
      GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] |
      UNIQUE index_parameters |
      PRIMARY KEY index_parameters |
      REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
        [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
    [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

    而table_constraint是:

    [ CONSTRAINT constraint_name ]
    { CHECK ( expression ) [ NO INHERIT ] |
      UNIQUE ( column_name [, ... ] ) index_parameters |
      PRIMARY KEY ( column_name [, ... ] ) index_parameters |
      EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |
      FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
        [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE action ] [ ON UPDATE action ] }
    [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

    并且 table_constraint_using_index 是:

        [ CONSTRAINT constraint_name ]
        { UNIQUE | PRIMARY KEY } USING INDEX index_name
        [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

    UNIQUE、PRIMARY KEY以及EXCLUDE约束中的index_parameters是:

    [ INCLUDE ( column_name [, ... ] ) ]
    [ WITH ( storage_parameter [= value] [, ... ] ) ]
    [ USING INDEX TABLESPACE tablespace_name ]

    exclude_element in an EXCLUDE constraint is:

    { column_name | ( expression ) } [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]

如果需要增加一个新的约束但是不希望长时间阻塞表更新,那么使用现有索引增加约束会有所帮助。要这样做,用 CREATE INDEX CONCURRENTLY创建该索引,并且接着使用这种语法把它安装为一个正式的约束。例子见下文。

ALTER CONSTRAINT这种形式修改之前创建的一个约束的属性。当前只能修改外键约束。

VALIDATE CONSTRAINT这种形式验证之前创建为NOT VALID的外键或检查约束, 它会扫描表来确保对于该约束没有行不满足约束。如果约束已经被标记为合法,则什么也不会发生。

DROP CONSTRAINT [ IF EXISTS ]这种形式在一个表上删除指定的约束,还有位于该约束之下的任何索引。如果IF EXISTS 被指定并且该约束不存在,不会抛出错误。在这种情况下会发出一个提示。

DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER这些形式配置属于该表的触发器的触发设置。系统仍然知道被禁用触发器的存在,但是即使它的触发事件发生也不会执行它。对于一个延迟触发器, 会在事件发生时而不是触发器函数真正被执行时检查其启用状态。可以禁用或者启用名称指定的单个触发器、表上的所有触发器、用户拥有的触发器(这个选项会排除内部生成的约束触发器,例如用来实现外键约束或可延迟唯一和排除约束)。禁用或者启用内部生成的约束触发器要求超级用户特权,这样做要小心因为如果这类触发器不被执行,约束的完整性当然无法保证。

触发器引发机制也受到配置变量 session_replication_role 的影响。当复制角色是 “origin”(默认)或者“local”时,被简单启用的触发器将被触发。被配置为ENABLE REPLICA的触发器只有在会话处于“replica”模式时才将被触发。被配置为 ENABLE ALWAYS的触发器的触发不会考虑当前复制角色。

这种机制的效果就是,在默认配置中,触发器不会在复制体上引发。这种效果很有用,因为如果一个触发器在源头上被用来在表之间传播数据,那么复制系统也将复制被传播的数据,并且触发器不应该在复制体上引发第二次,因为那会导致重复。不过,如果一个触发器被用于另一种目的(例如创建外部告警),那么将它设置为ENABLE ALWAYS可能更加合适,这样它在复制体上也会被引发。

这个命令要求一个SHARE ROW EXCLUSIVE锁。

DISABLE/ENABLE [ REPLICA | ALWAYS ] RULE这些形式配置属于表的重写规则的触发设置。系统仍然知道一个被禁用规则的存在,但在查询重写时不会应用它。其语义与禁用的/启用的触发器的一样。 对于ON SELECT规则会忽略这个配置,即使当前会话处于一种非默认的复制角色,这类规则总是会被应用以保持视图工作正常。

规则引发机制也受到配置变量 session_replication_role 的影响,这和上述的触发器类似。

DISABLE/ENABLE ROW LEVEL SECURITY这些形式控制属于该表的行安全性策略的应用。如果被启用并且该表上不存在策略,则将应用一个默认否定的策略。即使行级安全性被禁用,在表上还是可以存在策略。在这种情况下,这些策略将不会被应用并且会被忽略。

NO FORCE/FORCE ROW LEVEL SECURITY这些形式控制当用户是表拥有者时表上的行安全性策略的应用。如果被启用, 当用户是表拥有者时,行级安全性策略将被应用。如果被禁用(默认),则当用户是表拥有者时,行级安全性将不会被应用。

CLUSTER ON这种形式为未来的 CLUSTER 操作选择默认的索引。 它不会真正地对表进行聚簇。

改变聚簇选项要求一个SHARE UPDATE EXCLUSIVE锁。

SET WITHOUT CLUSTER这种形式从表中移除最近使用的 CLUSTER 索引说明。这会影响未来的不指定索引的聚簇操作。

改变聚簇选项要求一个SHARE UPDATE EXCLUSIVE锁。

SET WITHOUT OIDS向后兼容的语法,用于删除oid系统列。由于oid系统列无法再添加,所以不会有实际效果。

SET TABLESPACE这种形式把该表的表空间更改为指定的表空间并且把该表相关联的数据文件移动到新的表空间中。表上的索引(如果有)不会被移动,但是它们可以用额外的SET TABLESPACE命令单独移动。 当应用于分区表时,不会移动任何内容,但之后用CREATE TABLE PARTITION OF创建的任何分区将使用该表空间,除非被TABLESPACE子句覆盖。

当前数据库在一个表空间中的所有表可以用ALL IN TABLESPACE形式移动,这将会首先锁住所有将被移动的表然后逐个移动。这种形式也支持 OWNED BY,它将只移动指定角色所拥有的表。如果指定了NOWAIT选项,则命令将在无法立刻获得所有所需要的锁时失败。注意这个命令不移动系统目录;如果想要移动系统目录,应该用ALTER DATABASE或者显式的 ALTER TABLE调用。对于这种形式来说, information_schema关系不被认为是系统目录的一部分, 因此它们将会被移动。

SET { LOGGED | UNLOGGED }该ALTER将表在UNLOGGED与LOGGED之间进行转换,但不能用于临时表。

说明

由于当前架构无法支持UNLOGGED TABLE,因此在SET UNLOGGED操作时会出现报错。

SET ( storage_parameter [= value ] [, ... ] )这种形式为该表更改一个或者更多存储参数。可用的参数请见 Storage Parameters 的 CREATE TABLE 。这个命令将不会立刻修改表内容,这取决于重写表以得到想要的结果可能需要的参数。可以用 VACUUM FULL、 CLUSTER 或者 ALTER TABLE的一种形式来强制一次表重写。对于规划器相关的参数,更改将从该表下一次被锁定开始生效,因此当前执行的查询不会受到影响。

对 fillfactor、toast 以及 autovacuum 存储参数,将会拿取SHARE UPDATE EXCLUSIVE锁,就像计划器参数 parallel_workers

RESET ( storage_parameter [, ... ] )这种形式把一个或者更多存储参数重置到它们的默认值。和 SET一样,可能需要一次表重写来更新整个表。

INHERIT parent_table这种形式把目标表增加为指定父表的一个新子女。随后,针对父亲的查询将包括目标表中的记录。要被增加为一个子女,目标表必须已经包含和父表完全相同的列(也可以有额外的列)。这些列必须具有匹配的数据类型,并且如果它们在父表中具有NOT NULL约束,它们在子表中也必须有NOT NULL约束。

也必须把子表约束与所有父表的CHECK约束进行匹配, 不过父表中那些被标记为非可继承(也就是用ALTER TABLE ... ADD CONSTRAINT ... NO INHERIT 创建的)除外,它们会被忽略。所有匹配得上的子表约束不能被标记为不可继承。当前,UNIQUEPRIMARY KEY 以及FOREIGN KEY约束没有被考虑,但是这种情况可能会在未来发生变化。

NO INHERIT parent_table这种形式把目标表从指定父表的子女列表中移除。针对父表的查询将不再包括来自目标表的记录。

OF type_name这种形式把该表链接到一种组合类型,就好像CREATE TABLE OF所做的那样。该表的列名和类型列表必须精确地匹配该组合类型。 该表必须不从任何其他表继承。这些限制确保CREATE TABLE OF能允许一个等价的表定义。

NOT OF这种形式解除一个有类型的表和其类型之间的关联。

OWNER TO这种形式把表、序列、视图、物化视图或外部表的拥有者改为指定用户。

REPLICA IDENTITY这种形式更改被写入到预写式日志来标识被更新或删除行的信息。除非使用逻辑复制, 这个选项将不会产生效果。DEFAULT(非系统表的默认值)记录主键列 (如果有)的旧值。USING INDEX记录被所提到的索引所覆盖的列的旧值,该索引必须是唯一索引、不是部分索引、不是可延迟索引并且只包括被标记成 NOT NULL的列。FULL记录行中所有列的旧值。 NOTHING不记录有关旧行的任何信息(这是系统表的默认值)。在所有情况下,除非至少有一个要被记录的列在新旧行版本之间发生变化,将不记录旧值。

RENAMERENAME形式更改一个表(或者一个索引、序列、视图、物化视图或者外部表)的名称、表中一个列的名称或者表的一个约束的名称。在重命名一个具有底层索引的约束时,该索引也会被重命名。它对已存储的数据没有影响。

SET SCHEMA这种形式把该表移动到另一个模式中。相关的该表列拥有的索引、约束和序列也会被移动。

ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT }这种形式把一个已有表(自身也可能被分区)作为一个分区挂接到目标表。该表可以为特定的值使用FOR VALUES挂接为分区,或者用DEFAULT挂接为一个默认分区。 对于目标表中的每一个索引,在被挂接的表上都将创建一个响应的索引,如果已经存在等效的索引,该索引将被挂接到目标表的索引,就像执行了ALTER INDEX ATTACH PARTITION一样。 请注意,如果现有表是外表,如果目标表上有UNIQUE索引, 则当前不允许将表作为目标表的分区附加。对于目标表中存在的每个用户定义的行级触发器,将在附加表中创建一个对应的触发器。

一个使用FOR VALUES的分区使用与 CREATE TABLE 中 partition_bound_spec相同的语法。分区边界说明必须对应于目标表的分区策略以及分区键。要被挂接的表必须具有和目标表完全相同的所有列,并且不能有多出来的列,而且列的类型也必须匹配。此外,它必须有目标表上所有的NOT NULL以及CHECK约束。当前不考虑FOREIGN KEY约束。来自于父表的UNIQUEPRIMARY KEY约束将被创建在分区上(如果它们还不存在)。如果被挂接的表上的任何CHECK约束被标记为NO INHERIT,则命令将失败,这类约束必须被重建且重建时不能有NO INHERIT子句。

如果新分区是一个常规表,会执行一次全表扫描来检查表中现有行没有违背分区约束。 可以通过对表增加一个有效的CHECK约束来避免这种扫描,该约束可以在运行这个命令之前仅允许满足所需分区约束的行。 CHECK约束可以解决让表无需被扫描就能验证分区约束。但是,如果任一分区键是一个表达式并且该分区不接受NULL值,这种方式就无效了。 如果挂接一个不接受NULL值的列表分区,还应该为分区键列增加NOT NULL约束,除非它是一个表达式。

如果新分区是一个外部表,则不需要验证该外部表中的所有行遵守分区约束。

当一个表有默认分区时,定义新分区会更改默认分区的分区约束。默认分区不能包含任何需要被移动到新分区中的行,并且将被扫描以验证不存在那样的行。如果一个合适的CHECK约束存在,这种扫描(和新分区的扫描一样)可以被避免。还是和新分区的扫描一样,当默认分区是外部表时这种扫描总是会被跳过。

在父表上附加一个分区获得一个SHARE UPDATE EXCLUSIVE锁,除了要附加的表和默认分区上的ACCESS EXCLUSIVE 锁(如果有)。

DETACH PARTITION partition_name这种形式会分离目标表的指定分区。被分离的分区继续作为独立的表存在,但是与它之前挂接的表不再有任何联系。任何被挂接到目标表索引的索引也会被分离。 任何作为目标表中那些表的副本创建的表也会被删除。

除了RENAMESET SCHEMAATTACH PARTITIONDETACH PARTITION之外,所有形式的 ALTER TABLE 都作用在单个表上,前面这些形式可以被组合成一个多修改的列表被一起应用。例如,可以在一个命令中增加多个列并且/或者修改多个列的类型。对于大型表来说这会特别有用,因为只需要对表做一趟操作。

要使用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自动删除依赖于被删除列或约束的对象(例如引用该列的视图), 并且接着删除依赖于那些对象的所有对象。

RESTRICT如果有任何依赖对象时拒绝删除列或者约束。这是默认行为。

trigger_name一个要禁用或启用的触发器的名称。

ALL禁用或者启用属于该表的所有触发器(如果有任何触发器是内部产生的约束触发器则需要超级用户特权,例如那些被用来实现外键约束或者可延迟一致性和排他约束的触发器)。

USER禁用或者启用属于该表的所有触发器,内部产生的约束触发器(例如那些被用来实现外键约束或者可延迟一致性和排他约束的触发器)除外。

index_name一个现有索引的名称。

storage_parameter一个表存储参数的名称。

value一个表存储参数的新值。根据该参数,该值可能是一个数字或者一个词。

parent_table要与这个表关联或者解除关联的父表。

new_owner该表的新拥有者的用户名。

new_tablespace要把该表移入其中的表空间的名称。

new_schema要把该表移入其中的模式的名称。

partition_name要被作为新分区附着到这个表或者从这个表上分离的表的名称。

partition_bound_spec新分区的分区边界说明。

说明

关键词COLUMN可以被省略。

在使用ADD COLUMN增加一列并且指定了一个非易失性DEFAULT时,默认值会在该语句执行时计算并且结果会被保存在表的元数据中。这个值将被用于所有现有行的该列。如果没有指定DEFAULT,则使用 NULL。在两种情况下都不需要重写表。

增加一个带有非易失性DEFAULT子句的列或者更改一个现有列的类型将要求重写整个表及其索引。在更改一个现有列的类型时有一种例外:如果 USING子句不更改列的内容并且旧类型在二进制上与新类型可强制转换或者是新类型上的一个未约束域,则不需要重写表。但是受影响列上的任何索引仍必须被重建。对于一个大型表,表和/或索引重建可能会消耗相当多的时间, 并且会临时要求差不多两倍的磁盘空间。

增加一个CHECK或者NOT NULL约束要求扫描表以验证现有行符合该约束,但是不要求一次表重写。

类似地,在挂接一个新分区时,它需要被扫描以验证现有行满足该分区约束。

提供在一个ALTER TABLE中指定多个更改的选项的主要原因就是多次表扫描或者重写可以因此被整合成一次。

扫描大型表以验证新的外键或检查约束可能需要很长时间,并且对表的其他更新将锁定,直到ALTER TABLE ADD CONSTRAINT命令被提交。 NOT VALID约束选项的主要目的是减少对并发更新添加约束的影响。 使用 NOT VALIDADD CONSTRAINT命令不扫描表,可以立即提交。 在之后,VALIDATE CONSTRAINT命令会被发出以验证现有行是否满足约束。 验证步骤不需要锁定并发更新,因为它知道其他事务将强制执行它们插入或更新的行的约束;只有预先存在的行需要检查。 因此,验证在被更改的表上仅获得一个SHARE UPDATE EXCLUSIVE锁。 (如果约束是外键,则 ROW SHARE锁也需要约束的表引用。) 除了改进并发性外,在已知该表包含预先存在的违规行为的情况下使用 NOT VALIDVALIDATE CONSTRAINT也能有作用。 一旦约束就位,就不能再插入新的违规,并且现有问题可以在空闲时纠正,直到VALIDATE CONSTRAINT最终完成。

DROP COLUMN形式不会在物理上移除列,而只是简单地让它对 SQL 操作不可见。后续该表中的插入和更新操作将为该列存储一个空值。因此,删除一个列很快,但是它不会立刻减少表所占的磁盘空间, 因为被删除列所占用的空间还没有被回收。随着现有列被更新,空间将被逐渐回收。

要强制立即回收被已删除列占据的空间,你可以执行一种能导致全表重写的 ALTER TABLE形式。这种形式会导致重新构造每一个把被删除列替换为空值的行。

ALTER TABLE的重写形式对于 MVCC 是不安全的。 在一次表重写之后,如果并发事务使用的是一个在重写发生前取得的快照,该表将对这些并发事务呈现出空表的形态。

SET DATA TYPEUSING选项能实际指定涉及该列旧值的任何表达式。也就是说,它可以不但可以引用要被转换的列, 还可以引用其他列。这允许使用SET DATA TYPE语法完成十分普遍的转换。由于这种灵活性,USING表达式不适合于列的默认值(如果有),结果可能不是一个默认值所需的常量表达式。这意味着在没有从旧类型到新类型的隐式或者赋值造型时,即便提供了一个 USING子句,SET DATA TYPE还是可能无法转换默认值。在这种情况下,用DROP DEFAULT删除该默认值, 执行ALTER TYPE并且接着使用SET DEFAULT增加一个合适的新默认值。类似的考虑也适用于涉及该列的索引和约束。

如果一个表有任何后代表,在不对后代表做相同操作的情况下,不允许在父表中增加列、重命名列或者更改列的类型。这确保了后代总是具有和父表匹配的列。类似地,如果不对所有后代上的CHECK约束进行重命名,就不能在父表中重命名该CHECK约束,这样CHECK约束也能在父表及其后代之间保持匹配(不过,这个限制不适用于基于索引的约束)。此外,因为从父表中选择也会从其后代中选择,父表上的约束不能被标记为有效,除非它在那些后代上也被标记为有效。在所有这些情况下,ALTER TABLE ONLY都将被拒绝。

只有当一个后代表的列不是从任何其他父表继承而来并且没有该列的独立定义时, 一次递归的DROP COLUMN操作才会移除该列。一次非递归的DROP COLUMN(即 ALTER TABLE ONLY ... DROP COLUMN)不会移除任何后代列,而是会把它们标记成独立定义的列。对于一个分区表,一个非递归的DROP COLUMN命令将会失败,因为一个表的所有分区都必须有和分区根节点相同的列。

标识列的动作(ADD GENERATEDSET等、DROP IDENTITY)以及动作TRIGGERCLUSTEROWNERTABLESPACE不会递归到后代表上,也就是说它们执行时总是好像指定了ONLY一样。增加约束的动作仅对没有标记为NO INHERITCHECK约束递归。

不允许更改一个系统目录表的任何部分。

示例

向一个表增加一个类型为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);

通过一个USING子句更改一个包含 Unix 时间戳的整数列为 timestamp with time zone

    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;