Включение нескольких столбцов в один индекс в Postgres - PullRequest
3 голосов
/ 02 июля 2011

У меня есть таблица 'users' с двумя столбцами, 'email' и 'new_email'.Мне нужно:

  • Ограничение уникальности без учета регистра, охватывающее оба столбца - т. Е. Если «Bob@Example.com» появляется в столбце «email» одной строки, то вставляется пример «bob @».com "в столбец 'new_email' другой строки (или даже той же строки) должен произойти сбой.

  • Быстрый поиск без учета регистра указанного адреса электронной почты в« email »или« email ».Поля new_email '- то есть найдите строку, в которой адрес электронной почты new_email ИЛИ "Bob@example.com", без учета регистра.

Я знаю, что я мог бы сделать это проще, создавсвязанная таблица «электронных писем», но я ожидаю, что в этой таблице будут искать пользователей (по первичному ключу) из нескольких приложений, и я хотел бы избежать дублирования логики объединения в различных местах, чтобы также получать их электронные письма.Поэтому я думаю, что лучше использовать какой-то индекс выражения, если это возможно.

Если это невозможно, я полагаю, что моим следующим лучшим вариантом будет создание представления, которое другие приложения могли бы использовать для простого извлеченияэлектронные письма пользователя вместе с другой информацией, но я не уверен, как это сделать.

Я использую Postgres 8.4.Спасибо!

Ответы [ 2 ]

4 голосов
/ 02 июля 2011

Я думаю, вам придется использовать триггер, чтобы применить ограничение уникальности между столбцами. Если вы добавляете уникальные индексы для каждого столбца, а затем запускаете что-то вроде этого (непроверенное в верхней части кода моей головы):

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. Этот триггер позаботится о перехвате дубликатов между столбцами, а уникальные индексы позаботятся о дубликатах в столбцах.

Некоторые полезные ссылки:

В любом случае вам потребуются отдельные индексы для ваших запросов, и использование половины индексов уникальности упрощает ваш триггер, оставляя его для работы только с частью столбца; если вы попытаетесь сделать все это в триггере, вам придется следить за обновлением строки, не меняя столбцы email или email_new.

Для половины запроса вы можете создать представление , в котором для объединения двух столбцов использовалось UNION. Вы также можете создать функцию для объединения адресов электронной почты пользователя в один список. Трудно сказать, что было бы лучше без знания более подробной информации об этих других запросах, но я подозреваю, что исправление всех других запросов, чтобы узнать о email и email_new, было бы лучшим подходом; вам все равно придется обновить все остальные запросы, чтобы использовать представление или функцию, так зачем вообще создавать представление или функцию?

0 голосов
/ 03 июля 2011

Нет необходимости в триггерах. Попробуйте это:

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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...