表结构修改
当你创建一个表后,如果发现有错误,或者应用程序的需求发生变化,你可以删除这个表,然后重新创建它。但是,如果表已经包含了数据,或者表被其他数据库对象引用(比如外键约束),这就不再是一个方便的选择。因此,ProtonBase 提供了一组命令来修改已有的表。需要注意的是,这在概念上与修改表中的数据是不同的:我们关心的是修改表的定义或结构。
你可以进行以下操作:
所有这些操作都使用 ALTER TABLE
命令来执行,更详细的信息使用方法请参考 Modifying Tables
,这里提供的是基本信息。
添加列
假设原始表是通过以下命令创建的:
CREATE TABLE products (
product_no integer NOT NULL,
name text NOT NULL,
price numeric
);
要添加一列,可以使用如下命令:
ALTER TABLE products ADD COLUMN description text;
新的列最初会被填充为给定的默认值(如果没有指定 DEFAULT
子句,则为 NULL
)。
注意:如果添加的列具备一个确定性的默认值,ProtonBase 会避免在执行
ALTER TABLE
语句时就将更新表的每一行。相反,这个列会在下次访问时返回默认值。这样即使在一张很大的表上,ALTER TABLE
也会非常快速。
然而,如果默认值是不稳定的(例如,clock_timestamp()
),则 ALTER TABLE
执行时则需要更新表的每一行。ProtonBase 会尝试使用 Online Schema Change 的技术来避免该表被长时间的锁住而导致不可读写。
你还可以同时在列上定义约束,使用通常的语法:
ALTER TABLE products ADD COLUMN description text CHECK (description <> '');
实际上,在 CREATE TABLE
中可以应用到该列的所有选项都可以在这里使用。然而请注意,默认值必须满足给定的约束,否则添加操作将失败。或者,你可以稍后添加约束(见下文),在你正确填充了新列之后。
最佳实践:
- 添加列时考虑默认值:如果可能,为新列提供一个合理的默认值,以避免
NULL
值对现有查询的影响。- 避免在大表上添加带约束的列:在大表上添加带约束的列可能需要很长时间,建议先添加列,再单独添加约束。
删除列
要删除一列,可以使用如下命令:
ALTER TABLE products DROP COLUMN description;
该列中的所有数据都会被删除。涉及该列的表约束也会被删除。然而,如果该列被另一个表的外键约束引用,ProtonBase 不会自动删除该约束。你可以通过添加 CASCADE
授权删除依赖于该列的所有内容:
ALTER TABLE products DROP COLUMN description CASCADE;
重要提示:删除列是不可逆操作,请在执行前确认是否真的不需要该列的数据。如果该列被其他表的外键引用,使用
CASCADE
会删除所有依赖项,请谨慎使用。
修改主键
要修改表的主键,可以使用如下命令:
如果表里原来没有主键,并且候选主键列满足非空且所有值唯一的条件,可以将该列添加为主键。注意添加主键会锁表,如果表里数据多,会扫描全表进行唯一性验证,建议考虑在业务低峰期操作。
-- 为现有列(product_no)添加主键
ALTER TABLE products ADD PRIMARY KEY (product_no);
也支持对已有主键的表,修改主键列为其他字段。
BEGIN;
ALTER TABLE products DROP CONSTRAINT products_pkey;
ALTER TABLE products ADD PRIMARY KEY (name);
COMMIT;
重要提醒:修改主键是一个高风险操作,可能会影响依赖该表的所有查询和应用。在生产环境中修改主键前,务必进行充分的测试。
添加约束
要添加约束,使用表约束的语法。例如:
ALTER TABLE products ADD CHECK (name <> '');
ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no);
ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups;
要添加一个不为空约束,这不能写为表约束,需要使用以下语法:
ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;
约束将在语句的时候被检查,因此在添加之前,表的数据必须满足约束,否则命令将失败。
最佳实践:
- 添加约束前,先检查现有数据是否满足约束条件。
- 对于大表,考虑在低峰期添加约束以减少对性能的影响。
删除约束
要删除约束,你需要知道它的名称。如果你为它命名了,那就很容易。否则,系统会分配一个生成的名称,你需要找出它。在这里,psql 命令 \d tablename
可以提供帮助;其他接口可能也提供了一种查看表详细信息的方式。然后,使用以下命令:
ALTER TABLE products DROP CONSTRAINT some_name;
注意:如果你正在处理像
$2
这样的生成的约束名称,请不要忘记你需要将其用双引号括起来,使其成为一个有效的标识符。
与删除列类似,如果你想删除某些依赖于约束的内容,你需要添加 CASCADE
。例如,外键约束依赖于被引用列上的唯一约束或主键约束。
对于非空约束以外的所有约束类型,操作方式都是相同的。要删除非空约束,请使用以下命令:
ALTER TABLE products ALTER COLUMN product_no DROP NOT NULL;
注意:非空约束没有名称。
修改默认值
要为列设置新的默认值,可以使用以下命令:
ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;
这不会影响表中的任何现有行,它只是改变了将来执行的 INSERT
命令的默认值。
要删除任何默认值,请使用以下命令:
ALTER TABLE products ALTER COLUMN price DROP DEFAULT;
这实际上与将默认值设置为 NULL
是一样的。因此,如果没有定义默认值,删除一个默认值并不会报错,因为默认值隐式地是 NULL
值。
修改列的数据类型
要将列转换为不同的数据类型,可以使用以下命令:
ALTER TABLE products ALTER COLUMN price TYPE numeric(10,2);
只有当该列中的每个现有条目都可以通过隐式转换转换为新类型时,此操作才会成功。如果需要更复杂的转换,你可以添加 USING
子句,该子句指定如何从旧值计算新值。
ProtonBase 将尝试将该列的默认值(如果有)转换为新类型,以及任何涉及该列的约束。但是,这些转换可能失败,或者可能产生意想不到的结果。通常最好在修改列的类型之前删除该列上的任何约束,然后在之后添加适当修改后的约束。
注意事项:修改列的数据类型可能需要重写整个表,对于大表来说可能耗时较长。在执行前确保现有数据可以成功转换为目标类型。
重命名列
要重命名列:
ALTER TABLE products RENAME COLUMN product_no TO product_number;
重命名表
要重命名一个表:
ALTER TABLE products RENAME TO items;
注意事项:重命名表或列会影响所有引用它们的查询、视图和存储过程。在生产环境中执行重命名操作前,请确保更新所有相关的应用代码。