PRIMARY KEY实际上意味着什么,我的桌子需要一个?

时间:2014-07-31 21:00:38

标签: sql database postgresql database-schema postgresql-9.3

我有一个带有用户表的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));

但是我会有两个索引,这对我来说似乎是浪费和不必要的。那么,PRIMARY KEY是否意味着什么特别的东西超出" UNIQUE NOT NULL,"没有一个我错过了什么?

3 个答案:

答案 0 :(得分:4)

首先,实际上 每个 表都应该有一个主键。

citext

附加模块提供相同名称的数据类型。 “ci”表示不区分大小写。每个文档:

  

citext模块提供不区分大小写的字符串类型,   citext。从本质上讲,它在比较时会在内部调用lower   值。否则,它的行为几乎与text完全相同。

它的目的完全是您描述的目的:

  

citext数据类型允许您消除SQL中较低的调用   查询,并允许主键不区分大小写

大胆强调我的 请务必先阅读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));

integer的基础serial小而快,不必浪费时间lower()或数据库整理。这对外键引用特别有用。我更喜欢那些具有不同属性的自然主键。

这两种解决方案都有利有弊。

答案 1 :(得分:3)

我建议使用主键,因为你已经说过你想要一些独特的东西,并且你已经证明你可以对用户名设置唯一的约束。我将假设,因为这是一个唯一的非空用户名,您将使用它来跟踪数据库其他部分中的用户,以及允许更改用户名。 这是主键将派上用场的地方,而不是必须进入所有表并更改用户名列的值,您将只有一个地方可以更改它。 实施例

   Without primary key:
   Table users
   Username
   'Test'


   Table thingsdonebyUsers
   RandomColumn AnotherColumn  Username
   RandomValue   RandomValue    Test

现在假设您的用户想要将他的用户名更改为Test1,现在您必须在您使用用户名的任何地方找到它并将其更改为新值,然后在您的用户表中更改它,因为我假设您将拥有约束那里。

  With Primary Key
  Table users
  PK           Username
  1              'Test'


  Table thingsdonebyUsers
  RandomColumn   AnotherColumn     PK_Users
  RandomValue    RandomValue         1

现在您只需更改用户表并完成更改即可。 如您所示,您仍然可以在用户名栏上强制执行唯一而非空。 这只是规范化表的众多优点之一,它要求您的表具有一个不相关的主键(现在忘记正确的名称)。

至于PK实际上表示什么,它只是一个不可为空的唯一列,用于标识行,因此从这个意义上讲,您的表上已经有了一个主键。事情是,由于我上面解释的原因,通常PK是INT编号。

答案 2 :(得分:0)

简短的回答:不,你不需要声明性的" PRIMARY KEY",因为UNIQUE索引具有相同的目的。

答案很长:

使用主密钥的想法来自数据库系统,其中数据按物理顺序排列。这需要一个单一的"主要"键。 MySQL InnoDB就是这种方式,许多旧数据库也是如此。

但是,PostgreSQL不会按顺序保持表格;它将索引(包括主键索引)与堆分开,这基本上是无序的。因此,在Postgres中,主键和唯一索引之间没有实质性差异。您甚至可以针对唯一索引创建外键,只要该索引覆盖整个表。

话虽这么说,PostgreSQL外部的一些工具会查找主键,并且不会将唯一索引视为等效。由于没有找到PK,这些工具可能会导致您的问题。