在幕后,枚举值是real
,而不是integer
,这就是它占用4个字节的原因.请参阅系统目录:
\d pg_enum
Table "pg_catalog.pg_enum"
Column │ Type │ Collation │ Nullable │ Default
═══════════════╪══════╪═══════════╪══════════╪═════════
oid │ oid │ │ not null │
enumtypid │ oid │ │ not null │
enumsortorder │ real │ │ not null │
enumlabel │ name │ │ not null │
Indexes:
"pg_enum_oid_index" PRIMARY KEY, btree (oid)
"pg_enum_typid_label_index" UNIQUE CONSTRAINT, btree (enumtypid, enumlabel)
"pg_enum_typid_sortorder_index" UNIQUE CONSTRAINT, btree (enumtypid, enumsortorder)
enumtypid
是数据类型的OID(从pg_type
开始),enumsortorder
是表中存储的实际值,enumlabel
是标签.
要理解为什么使用real
,请看下面的例子:
CREATE TYPE wealth AS ENUM ('poor', 'rich');
SELECT * FROM pg_enum WHERE enumtypid = 'wealth'::regtype;
oid │ enumtypid │ enumsortorder │ enumlabel
═══════╪═══════════╪═══════════════╪═══════════
35366 │ 35364 │ 1 │ poor
35368 │ 35364 │ 2 │ rich
(2 rows)
到目前为止,这并不是很令人惊讶.
ALTER TYPE wealth ADD VALUE 'billionaire';
SELECT * FROM pg_enum WHERE enumtypid = 'wealth'::regtype;
oid │ enumtypid │ enumsortorder │ enumlabel
═══════╪═══════════╪═══════════════╪═════════════
35366 │ 35364 │ 1 │ poor
35368 │ 35364 │ 2 │ rich
35370 │ 35364 │ 3 │ billionaire
(3 rows)
这一点也很清楚.
ALTER TYPE wealth ADD VALUE 'millionaire' BEFORE 'billionaire';
SELECT * FROM pg_enum WHERE enumtypid = 'wealth'::regtype;
oid │ enumtypid │ enumsortorder │ enumlabel
═══════╪═══════════╪═══════════════╪═════════════
35366 │ 35364 │ 1 │ poor
35368 │ 35364 │ 2 │ rich
35370 │ 35364 │ 3 │ billionaire
35371 │ 35364 │ 2.5 │ millionaire
(4 rows)
阿!从'millionaire' < 'billionaire'
开始,其排序顺序必须介于'rich'
和'billionaire'
之间.对于integer
,我们必须将排序顺序从'billionaire'
更改为4,以便为新标签腾出空间.但这是不可能的,因为内部值3可能已经存储在某个表中,我们当然不能重写所有这些表.
显而易见的答案是使用浮点值,这样我们就可以在现有值之间插入新值.
我们必须"浪费"四个字节,因为没有更短的浮点数据类型.如果您考虑到每个表行都有23个字节的开销,并且您想要节省的3个字节中的很大一部分很可能由于对齐要求而丢失到填充中,那么您将看到浪费并不是那么糟糕.