在Postgres的单个索引中包含多个列

时间:2011-07-02 18:03:52

标签: sql postgresql indexing

我有一个'用户'表,其中有两列,'email'和'new_email'。我需要:

  • 一个不区分大小写的唯一性约束,涵盖两个列 - 即,如果“Bob@Example.com”出现在一行的“email”列中,然后将“bob@example.com”插入另一行(或甚至同一行的'new_email'列应该失败。

  • 在“email”或“new_email”字段中快速不区分大小写搜索给定的电子邮件地址 - 即查找new_email或电子邮件为“Bob@example.com”的行,不区分大小写。

我知道我可以通过创建相关的“电子邮件”表来更轻松地完成此操作,但我希望能够从多个应用程序中查找此表中的用户(通过主键),我想避免在各个地方复制连接逻辑以检索他们的电子邮件。所以我认为某种表达式索引最好,如果可能的话。

如果这是不可能的,我想我的下一个最佳选择是创建一个其他应用程序可以用来轻松获取用户的电子邮件及其他信息的视图,但我不知道该怎么做任

我正在使用Postgres 8.4。谢谢!

2 个答案:

答案 0 :(得分:4)

我认为您必须使用触发器来强制执行跨列唯一性约束。如果你在每一列上添加唯一索引,然后在这样的触发器上添加(在我的头部代码顶部未经测试):

CREATE FUNCTION no_dups_allowed() RETURNS trigger AS $$
DECLARE
    r ROW;
BEGIN
    SELECT 1 INTO r
    FROM users
    WHERE LOWER(email)     = LOWER(NEW.email_new)
       OR LOWER(email_new) = LOWER(NEW.email);
    IF FOUND THEN
        -- Found a duplicate so it is time for a hissy fit!
        RAISE 'Duplicate email address found' USING ERRCODE = 'unique_violation';
    END;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

你想要这样的东西作为BEFORE INSERT和BEFORE UPDATE触发器。该触发器将负责捕获跨列重复项,并且唯一索引将处理列内重复项。

一些有用的参考资料:

无论如何,您将需要查询的各个索引,并且使用索引的一半唯一性通过使其仅处理交叉列部分来简化您的触发器;如果您尝试在触发器中执行所有操作,那么您必须注意更新行而不更改emailemail_new列。

对于查询的一半,您可以create a view使用UNION来组合这两列。您还可以创建一个功能,将用户的电子邮件地址合并到一个列表中。很难说在不知道这些其他查询的更多细节的情况下最好哪些但我怀疑修复所有其他查询以了解emailemail_new将是最好的方法;您还必须更新所有其他查询以使用视图或函数,以便为什么要构建视图或函数?

答案 1 :(得分:0)

无需触发器。试试这个:

create  table et (email text, email2 text);
create unique index et_u on et (coalesce(lower(email),lower(email2)));
insert into et (email,email2) values ('scott@gmail.com',NULL);
insert into et (email,email2) values ('scott@gmail.com',NULL);
ERROR:  duplicate key value violates unique constraint "et_u"
insert into et (email,email2) values (NULL,'scott@gmail.com');
ERROR:  duplicate key value violates unique constraint "et_u"
insert into et (email,email2) values (NULL,'Scott@gmail.com');
ERROR:  duplicate key value violates unique constraint "et_u"