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 约束。详情:

Any downsides of using data type "text" for storing strings? Change PostgreSQL columns used in views

连同您原始设计中的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 with ON UPDATE CASCADE 负责自动更新相关条目。尽管如此,更改多个依赖行的成本更高......【参考方案3】:

简短回答:不,您不需要声明性“主键”,因为 UNIQUE 索引具有相同的确切目的。

长答案:

拥有主键的想法来自数据在物理上按键顺序排列的数据库系统。这需要有一个单一的“主”键。 mysql InnoDB 就是这种方式,许多旧数据库也是如此。

然而,PostgreSQL 不保持表的键顺序;它将索引(包括主键索引)从本质上是无序的堆中分离出来。因此,在 Postgres 中,主键和唯一索引之间没有本质区别。您甚至可以针对唯一索引创建外键,只要该索引覆盖整个表即可。

话虽如此,PostgreSQL 外部的一些工具会寻找主键,但并不认为唯一索引是等价的。这些工具可能会因为找不到 PK 而给您带来问题。

【讨论】:

以上是关于PRIMARY KEY 实际上意味着啥,我的桌子需要一个吗?的主要内容,如果未能解决你的问题,请参考以下文章

MySql里面的 PRIMARY KEY 和 KEY 是啥关系?一个字段即可以是PRIMARY KEY 又可以是 KEY吗?

mysql primary key有啥作用?

使用实体框架添加记录时违反链接记录的PRIMARY KEY约束

PRIMARY KEY (`id`) USING BTREE啥意思

java向数据库插入数据时的错误: Duplicate entry '' for key 'PRIMARY' 是啥问题,怎么解决,先谢啦!

primary key和unique key区别