我有一个带有用户表的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,"没有一个我错过了什么?
答案 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,这些工具可能会导致您的问题。