根据https://www.postgresql.org/docs/current/datatype-enum.html

枚举值在磁盘上占用四个字节.

这似乎是一个奇怪的 Select .当然,为了速度,枚举标签被映射到固定大小的整数,但是为什么是4个字节呢?

大多数情况下,枚举只用于少数选项;1个字节就足够了.

可以理解的是,设计师可能希望满足偶尔更大的枚举.在野外见过的最大的枚举是什么?我可以想象一个被用于例如国家代码.这是249最后一次计数;只有几个国家将溢出1字节.我可以看到将其设置为2字节的理由.

但为什么是4个呢?有没有人使用过超过65536个 case 的数据库枚举?Postgres真的和那么多人一起工作吗?

推荐答案

在幕后,枚举值是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个字节中的很大一部分很可能由于对齐要求而丢失到填充中,那么您将看到浪费并不是那么糟糕.

Database相关问答推荐

Kusto:从一个表中复制行并追加到同一集群中的另一个表中

如何限制报表中返回的行数?

Prisma - 将属性的类型设置为枚举数组

如何高效地存储棋局?

使用 prisma ORM 在我的迁移中手动添加触发器

在哪里存储 PHP 应用程序的数据库登录凭据

安装 SQL Server Management Studio Express后提示:Cannot open user default database. Login failed.

如何更改 Heroku 中的列类型?

为什么null不等于null false

从 XML 读取数据

恢复数据库备份时出错

Meteor 如何执行数据库迁移?

我应该不断地open()和close() SQL 数据库还是让它保持打开状态?

MySQL 转储所有数据库并在导入时创建(或重新创建)它们?

SQL Server 2005 是否具有与 MySql 的 ENUM 数据类型等效的数据类型?

EF4 代码优先导致 InvalidOperationException

如何动态更改 Ruby on Rails 中所有模型的 Active Record 数据库?

我如何做大于/小于使用 MongoDB?

如何从多个表中 Select 不同的值

在 UI 中执行业务逻辑的单元测试数据库应用程序