在 Postgresql 9.3 中将字符串列转换为数组列
Convert String Column to Array Column in Postrgresql 9.3
我在数据库中有一个字符串列需要转换为数组类型。在我还需要设置索引的过程中不要锁定数据库。
ALTER TABLE sites ALTER COLUMN rtb_id TYPE varchar[] USING string_to_array(rtb_id, '');
CREATE INDEX CONCURRENTLY rtb_id_search ON sites(rtb_id) USING array_to_string;
DROP INDEX CONCURRENTLY ix_sites_bundle_trgm_gin ON sites;
DROP INDEX CONCURRENTLY ix_sites_name_trgm_gin ON sites;
是这样吗?
编辑:
ALTER TABLE sites ADD COLUMN rtb_ids varchar[]
...
BEFORE INSERT OR UPDATE ... FOR EACH ROW trigger that sets NEW.rtb_id_new := string_to_array(NEW.rtb_id,' ') for each row.
In batches, UPDATE sites SET rtb_id_new = string_to_array(rtb_id,' ')
...
VACUUM sites;
CREATE INDEX CONCURRENTLY rtb_ids_search ON sites(rtb_ids) USING array_to_string(rtb_ids, '');
ALTER TABLE sites DROP COLUMN rtb_id;
谢谢
没有锁不可能。不过,您可以使用相对较少的短期强锁来实现。
ALTER TABLE
目前将占用排他锁很长一段时间,因为它进行了完全 table 重写。
相反,您需要:
ALTER TABLE sites ADD COLUMN rtb_id_new varchar[]
- 创建一个
BEFORE INSERT OR UPDATE ... FOR EACH ROW
触发器,为每一行设置 NEW.rtb_id_new := string_to_array(NEW.rtb_id,' ')
。
- 分批,
UPDATE sites SET rtb_id_new = string_to_array(rtb_id,' ')
- 填充所有值后
VACUUM sites;
然后 ALTER TABLE sites ALTER COLUMN rtb_id_new NOT NULL
。这将需要足够长的独占锁来进行顺序扫描,所以它不会超快。在 PostgreSQL 9.5 上,获取的锁较弱并且不会停止 SELECT
s.
- 建立索引
CONCURRENTLY
ALTER TABLE sites DROP COLUMN rtb_id; ALTER TABLE sites RENAME COLUMN rtb_id_new TO rtb_column;
- 如果您需要添加任何
UNIQUE
约束,请将它们添加 USING
已构建的索引以最小化锁定持续时间。
这不是完全无锁的。特别是 NOT NULL
约束会受到伤害,因为 PostgreSQL(还)不知道如何将 NOT NULL
约束添加为 NOT VALID
然后验证它。
我在数据库中有一个字符串列需要转换为数组类型。在我还需要设置索引的过程中不要锁定数据库。
ALTER TABLE sites ALTER COLUMN rtb_id TYPE varchar[] USING string_to_array(rtb_id, '');
CREATE INDEX CONCURRENTLY rtb_id_search ON sites(rtb_id) USING array_to_string;
DROP INDEX CONCURRENTLY ix_sites_bundle_trgm_gin ON sites;
DROP INDEX CONCURRENTLY ix_sites_name_trgm_gin ON sites;
是这样吗?
编辑:
ALTER TABLE sites ADD COLUMN rtb_ids varchar[]
...
BEFORE INSERT OR UPDATE ... FOR EACH ROW trigger that sets NEW.rtb_id_new := string_to_array(NEW.rtb_id,' ') for each row.
In batches, UPDATE sites SET rtb_id_new = string_to_array(rtb_id,' ')
...
VACUUM sites;
CREATE INDEX CONCURRENTLY rtb_ids_search ON sites(rtb_ids) USING array_to_string(rtb_ids, '');
ALTER TABLE sites DROP COLUMN rtb_id;
谢谢
没有锁不可能。不过,您可以使用相对较少的短期强锁来实现。
ALTER TABLE
目前将占用排他锁很长一段时间,因为它进行了完全 table 重写。
相反,您需要:
ALTER TABLE sites ADD COLUMN rtb_id_new varchar[]
- 创建一个
BEFORE INSERT OR UPDATE ... FOR EACH ROW
触发器,为每一行设置NEW.rtb_id_new := string_to_array(NEW.rtb_id,' ')
。 - 分批,
UPDATE sites SET rtb_id_new = string_to_array(rtb_id,' ')
- 填充所有值后
VACUUM sites;
然后ALTER TABLE sites ALTER COLUMN rtb_id_new NOT NULL
。这将需要足够长的独占锁来进行顺序扫描,所以它不会超快。在 PostgreSQL 9.5 上,获取的锁较弱并且不会停止SELECT
s. - 建立索引
CONCURRENTLY
ALTER TABLE sites DROP COLUMN rtb_id; ALTER TABLE sites RENAME COLUMN rtb_id_new TO rtb_column;
- 如果您需要添加任何
UNIQUE
约束,请将它们添加USING
已构建的索引以最小化锁定持续时间。
这不是完全无锁的。特别是 NOT NULL
约束会受到伤害,因为 PostgreSQL(还)不知道如何将 NOT NULL
约束添加为 NOT VALID
然后验证它。