如何在不锁定 table 的情况下向 Postgres 中的 ENUM 添加新值?
How can I add a new value to an ENUM in Postgres without locking the table?
我试过两种方法。
方法 1:使用添加的新值创建一个新的 ENUM 并就地切换数据类型:
-- Rename existing enum
ALTER TYPE animal_species RENAME TO animal_species_old;
-- Create new enum with new value
CREATE TYPE animal_species AS ENUM (
'dog',
'cat',
'elephant'
);
-- Update the column of Animals to use the new enum
ALTER TABLE "Animals" ALTER COLUMN species SET DATA TYPE animal_species USING species::text::animal_species;
DROP TYPE animal_species_old;
方法 2:使用临时列
-- Create new enum type with a new name (this will be the name of the enum from now on)
CREATE TYPE animal_type_enum AS ENUM (
'dog',
'cat',
'elephant'
);
-- Create a temporary column
ALTER TABLE "Animals" ADD COLUMN species_new animal_species_enum;
-- Copy existing species into new column
UPDATE "Animals" SET species_new = species::text::animal_species_enum;
-- Drop old species column
ALTER TABLE "Animals" DROP COLUMN species;
-- Rename new column
ALTER TABLE "Animals" RENAME COLUMN species_new TO species;
-- Drop old enum
DROP TYPE animal_species;
在这两种情况下,都创建了锁并关闭了我的应用程序。我相信第二种方式比第一种方式表现更好,但停机时间仍然是 unacceptable。 table 有数百万行。
请注意,我非常愿意使用 ENUM 以外的东西——我正在考虑在 [=27] 中创建一个带有外键 "species_id" 的 "Species" table =],但据我所知,这会产生相同的锁定问题(考虑到引入新的外键约束,可能会更糟)。
感谢您的帮助!
方法 3,只需向枚举添加一个新值:
ALTER TYPE animal_type_enum ADD VALUE 'snake';
如果您经常添加或删除新的查找值,单独的查找 table 是 much 更好的选择。
添加新值是一个简单的INSERT
操作,不会锁定任何东西(尤其是而不是table引用查找table).
虽然外键检查确实会增加一些开销,但它们应该无关紧要(假设 FK 列已正确索引),除非您非常频繁地执行批量 INSERT 或 DELETE。
对于单行 INSERT 或 DELETE(或仅 "hundreds" 行),您可能甚至不会注意到 FK 查找的开销 - 特别是如果查找 table 很小且仅包含几行。
如果你预料到重复值的风险,上面的例子可以利用 IF NOT EXISTS 来提供一些保护。
ALTER TYPE animal_type_enum ADD VALUE IF NOT EXISTS 'snake';
您将收到通知而不是错误。
我试过两种方法。
方法 1:使用添加的新值创建一个新的 ENUM 并就地切换数据类型:
-- Rename existing enum
ALTER TYPE animal_species RENAME TO animal_species_old;
-- Create new enum with new value
CREATE TYPE animal_species AS ENUM (
'dog',
'cat',
'elephant'
);
-- Update the column of Animals to use the new enum
ALTER TABLE "Animals" ALTER COLUMN species SET DATA TYPE animal_species USING species::text::animal_species;
DROP TYPE animal_species_old;
方法 2:使用临时列
-- Create new enum type with a new name (this will be the name of the enum from now on)
CREATE TYPE animal_type_enum AS ENUM (
'dog',
'cat',
'elephant'
);
-- Create a temporary column
ALTER TABLE "Animals" ADD COLUMN species_new animal_species_enum;
-- Copy existing species into new column
UPDATE "Animals" SET species_new = species::text::animal_species_enum;
-- Drop old species column
ALTER TABLE "Animals" DROP COLUMN species;
-- Rename new column
ALTER TABLE "Animals" RENAME COLUMN species_new TO species;
-- Drop old enum
DROP TYPE animal_species;
在这两种情况下,都创建了锁并关闭了我的应用程序。我相信第二种方式比第一种方式表现更好,但停机时间仍然是 unacceptable。 table 有数百万行。
请注意,我非常愿意使用 ENUM 以外的东西——我正在考虑在 [=27] 中创建一个带有外键 "species_id" 的 "Species" table =],但据我所知,这会产生相同的锁定问题(考虑到引入新的外键约束,可能会更糟)。
感谢您的帮助!
方法 3,只需向枚举添加一个新值:
ALTER TYPE animal_type_enum ADD VALUE 'snake';
如果您经常添加或删除新的查找值,单独的查找 table 是 much 更好的选择。
添加新值是一个简单的INSERT
操作,不会锁定任何东西(尤其是而不是table引用查找table).
虽然外键检查确实会增加一些开销,但它们应该无关紧要(假设 FK 列已正确索引),除非您非常频繁地执行批量 INSERT 或 DELETE。
对于单行 INSERT 或 DELETE(或仅 "hundreds" 行),您可能甚至不会注意到 FK 查找的开销 - 特别是如果查找 table 很小且仅包含几行。
如果你预料到重复值的风险,上面的例子可以利用 IF NOT EXISTS 来提供一些保护。
ALTER TYPE animal_type_enum ADD VALUE IF NOT EXISTS 'snake';
您将收到通知而不是错误。