如何在不锁定表的情况下向 Postgres 中的 ENUM 添加新值?

Jef*_*eff 4 sql postgresql enums denormalization

我尝试了两种方法。

方法 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;
Run Code Online (Sandbox Code Playgroud)

方法二:使用临时列

-- 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;
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,都创建了锁并使我的应用程序关闭。我相信第二种方式比第一种方式表现得更好,但停机时间仍然不可接受。该表有数百万行。

请注意,我非常愿意使用 ENUM 以外的其他东西——我正在考虑在“Animals”中创建一个带有外键“species_id”的“Species”表,但据我所知,这将创建相同的锁定问题(考虑到引入新的外键约束可能更糟)。

谢谢你的帮助!

a_h*_*ame 8

方法三,只需要给枚举添加一个新值:

ALTER TYPE animal_type_enum ADD VALUE 'snake';
Run Code Online (Sandbox Code Playgroud)

如果您经常添加或删除新的查找值,则单独的查找表非常有用更好的选择。

添加新值是一个简单的INSERT操作,不会锁定任何内容 (尤其是不会引用查找表的表)。

虽然外键检查确实增加了一些开销,但它们应该无关紧要(假设 FK 列已正确索引),除非您非常频繁地执行批量 INSERT 或 DELETE。

对于单行 INSERT 或 DELETE(或仅“数百”行),您甚至可能不会注意到 FK 查找的开销 - 特别是如果查找表很小且仅包含几行。


小智 6

如果您预计存在重复值的风险,上面的示例可以利用 IF NOT EXISTS 来提供一些保护。

ALTER TYPE animal_type_enum ADD VALUE IF NOT EXISTS 'snake';
Run Code Online (Sandbox Code Playgroud)

您将收到通知而不是错误。