PRIMARY KEY 实际上意味着啥,我的桌子需要一个吗?
Posted
技术标签:
【中文标题】PRIMARY KEY 实际上意味着啥,我的桌子需要一个吗?【英文标题】:What does PRIMARY KEY actually signify, and does my table need one?PRIMARY KEY 实际上意味着什么,我的桌子需要一个吗? 【发布时间】:2014-07-31 21:00:38 【问题描述】:我有一个 PostgreSQL 9.3 数据库,其中包含一个用户表,该表以保留大小写的格式存储用户名。所有查询都不区分大小写,所以我应该有一个支持它的索引。此外,无论大小写如何,用户名都必须是唯一的。
这是我想出的:
forum=> \d users
Table "public.users"
Column | Type | Modifiers
------------+--------------------------+------------------------
name | character varying(24) | not null
Indexes:
"users_lower_idx" UNIQUE, btree (lower(name::text))
以标准 SQL 语法表示:
CREATE TABLE users (
name varchar(24) NOT NULL
);
CREATE UNIQUE INDEX "users_lower_idx" ON users (lower(name));
有了这个架构,我已经满足了我的所有限制条件,尽管没有主键。 SQL标准不支持功能主键,所以不能提升索引:
forum=> ALTER TABLE users ADD PRIMARY KEY USING INDEX users_lower_idx;
ERROR: index "users_lower_idx" contains expressions
LINE 1: ALTER TABLE users ADD PRIMARY KEY USING INDEX users_lower_id...
^
DETAIL: Cannot create a primary key or unique constraint using such an index.
但是,我已经有了 UNIQUE 约束,并且该列已经被标记为“NOT NULL”。如果我必须有一个主键,我可以这样构造表:
CREATE TABLE users (
name varchar(24) PRIMARY KEY
);
CREATE UNIQUE INDEX "users_lower_idx" ON users (lower(name));
但是我将有两个索引,这对我来说似乎是浪费和不必要的。那么,除了“UNIQUE NOT NULL”之外,PRIMARY KEY 对 postgres 是否意味着任何特别的东西,我是否因为没有而错过了什么?
【问题讨论】:
您是否会拥有一个具有指向该表的外键的表?你的表只有一列吗?我想如果你的例子真的这么简单,那么你可能会侥幸逃脱。我以前在没有主键的环境中工作,那简直是一场噩梦。 还会有各种其他列,但为了简单起见,我在这里省略了它们。我确实打算让外键回到这个表,但是......嗯。我假设我可以引用具有唯一约束的列,但在这种情况下可能不是。让我试一试。 edit嘿,它确实有效! 【参考方案1】:首先,实际上每个表都应该有一个主键。
citext
附加模块提供同名的数据类型。 “ci”表示不区分大小写。根据文档:
citext
模块提供了不区分大小写的字符串类型,citext
。本质上,它在比较时内部调用lower
价值观。否则,它的行为几乎与text
完全相同。
它完全符合您描述的目的:
citext
数据类型允许您在 SQL 中消除对 lower 的调用 查询,并允许主键不区分大小写。
我的大胆强调。 请务必先阅读the manual about limitations。每个数据库安装一次
CREATE EXTENSION citext;
text
如果你不想走那条路,我建议你添加一个serial
作为代理主键。
CREATE TABLE users (
user_id serial PRIMARY KEY
, username text NOT NULL
);
我会使用text
而不是varchar(24)
。如果您需要强制执行最大长度(稍后可能会更改),请使用 CHECK
约束。详情:
连同您原始设计中的UNIQUE
索引(没有类型转换):
CREATE UNIQUE INDEX users_username_lower_idx ON users (lower(username));
serial
的底层integer
小而快,不必浪费时间处理lower()
或数据库的排序规则。这对于外键引用特别有用。我最喜欢它而不是一些具有不同属性的自然主键。
两种解决方案各有利弊。
【讨论】:
啊,出于某种原因,我很早就解雇了 citext。我很高兴你把它重新引起了我的注意,因为这似乎是正确的方法。你能详细说明为什么几乎每个表都应该有一个主键吗?据我了解,外键必须是主键或唯一键。在我的情况下,另一个表中的外键将指向 user.name,这是唯一的。 @AlexFrench:这是惯例,而不是规则。有几个函数默认使用主键,例如用于 FK 创建的语法简写。或者某些客户端需要主键。例如,pgAdmin 不允许您在没有 PK 的情况下编辑表。另一方面,我最近在不区分大小写的唯一用户名方面遇到了同样的问题。艰难的决定,但我选择了第二种解决方案。【参考方案2】:我建议使用主键,因为您已经说过您想要一些独特的东西,并且您已经证明您可以对用户名施加独特的约束。我假设这是一个唯一的、非空的用户名,您将使用它来跟踪数据库其他部分的用户,并允许更改用户名。 这是主键派上用场的地方,您不必进入所有表并更改 Username 列的值,您只需在一个地方进行更改。 示例
Without primary key:
Table users
Username
'Test'
Table thingsdonebyUsers
RandomColumn AnotherColumn Username
RandomValue RandomValue Test
现在假设您的用户想要将他的用户名更改为 Test1,那么现在您必须找到您使用 Username 的所有位置并将其更改为新值,然后再在您的用户表中更改它,因为我假设您将有一个约束在那里。
With Primary Key
Table users
PK Username
1 'Test'
Table thingsdonebyUsers
RandomColumn AnotherColumn PK_Users
RandomValue RandomValue 1
现在您只需更改您的用户表并完成更改。 正如您所演示的,您仍然可以在您的用户名列上强制执行唯一且不为空。 这只是拥有规范化表的众多优势之一,它要求您的表具有一个不相关值的主键(现在忘记它的正确名称)。
至于 PK 的实际含义,它只是一个标识行的不可为空的唯一列,因此从这个意义上说,您的表上已经有一个主键。问题是由于我上面解释的原因,通常 PK 是 INT 数字。
【讨论】:
啊,有趣,但就更改用户名而言,这不是必需的。 @Alex French like JChao 评论说,如果没有 FK 回到这张桌子,那么你的桌子设计就可以了。 值得一提的是foreign key constraints withON UPDATE CASCADE
负责自动更新相关条目。尽管如此,更改多个依赖行的成本更高......【参考方案3】:
简短回答:不,您不需要声明性“主键”,因为 UNIQUE 索引具有相同的确切目的。
长答案:
拥有主键的想法来自数据在物理上按键顺序排列的数据库系统。这需要有一个单一的“主”键。 mysql InnoDB 就是这种方式,许多旧数据库也是如此。
然而,PostgreSQL 不保持表的键顺序;它将索引(包括主键索引)从本质上是无序的堆中分离出来。因此,在 Postgres 中,主键和唯一索引之间没有本质区别。您甚至可以针对唯一索引创建外键,只要该索引覆盖整个表即可。
话虽如此,PostgreSQL 外部的一些工具会寻找主键,但并不认为唯一索引是等价的。这些工具可能会因为找不到 PK 而给您带来问题。
【讨论】:
以上是关于PRIMARY KEY 实际上意味着啥,我的桌子需要一个吗?的主要内容,如果未能解决你的问题,请参考以下文章
MySql里面的 PRIMARY KEY 和 KEY 是啥关系?一个字段即可以是PRIMARY KEY 又可以是 KEY吗?
使用实体框架添加记录时违反链接记录的PRIMARY KEY约束
PRIMARY KEY (`id`) USING BTREE啥意思
java向数据库插入数据时的错误: Duplicate entry '' for key 'PRIMARY' 是啥问题,怎么解决,先谢啦!