Добавьте ограничение уникальности в содержимое текстового массива Postgres [duplicate]

Перейдите к своему config.php. У меня такая же проблема. Проверьте имя пользователя и пароль, а также sql select - это то же имя, что и config.

16
задан Erwin Brandstetter 15 November 2015 в 23:38
поделиться

2 ответа

Праведный путь

Возможно, вам захочется пересмотреть нормализацию вашей схемы. Каждому не нужно «присоединяться даже к самому простому запросу». Создайте для этого VIEW .

Таблица может выглядеть так:

CREATE TABLE hostname (
 hostname_id serial PRIMARY KEY
,host_id     int    REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
,hostname    text   UNIQUE
);

Первичный первичный ключ hostname_id является опциональным . Я предпочитаю иметь его. В вашем случае hostname может быть основным ключом. Но многие операции выполняются быстрее с помощью простого маленького ключа integer. Создайте ограничение внешнего ключа для ссылки на таблицу host. Создайте представление следующим образом:

CREATE VIEW v_host AS
SELECT h.*
      ,array_agg(hn.hostname) AS hostnames
--    ,string_agg(hn.hostname, ', ') AS hostnames  -- text instead of array
FROM   host h
JOIN   hostname hn USING (host_id)
GROUP  BY h.host_id;   -- works in v9.1+

Начиная с pg 9.1, первичный ключ в GROUP BY охватывает все столбцы этой таблицы в списке SELECT. Замечания по выпуску для версии 9.1 :

Разрешить столбцы не GROUP BY в списке целей запроса, когда первичный ключ указан в предложении GROUP BY

Запросы могут использовать представление, подобное таблице. Поиск имени хоста будет намного быстрее:

SELECT *
FROM   host h
JOIN   hostname hn USING (host_id)
WHERE  hn.hostname = 'foobar';

Если у вас есть индекс на host(host_id), это должно быть так, как должно быть первичным ключом. Кроме того, ограничение UNIQUE на hostname(hostname) автоматически обновляет другой необходимый индекс.

В Postgres 9.2+ показатель многоколоночности будет еще лучше, если вы сможете получить только индексное сканирование :

CREATE INDEX hn_multi_idx ON hostname (hostname, host_id)

Начиная с Postgres 9.3, вы можете использовать MATERIALIZED VIEW , если позволяют обстоятельства. Особенно, если вы читаете гораздо чаще, чем вы пишете в таблицу.

Темная сторона (что вы на самом деле просили)

Если я не могу убедить вас в праведном пути, я Я тоже помогу на темной стороне. Я гибкий. :)

Вот демонстрация того, как обеспечить уникальность имен хостов. Я использую таблицу hostname для сбора имен хостов и триггера в таблице host, чтобы обновлять его.

Функция триггера

CREATE OR REPLACE FUNCTION trg_host_insupdelbef()
  RETURNS trigger AS
$func$
BEGIN
-- split UPDATE into DELETE & INSERT
IF TG_OP = 'UPDATE' THEN
   IF OLD.hostnames IS DISTINCT FROM NEW.hostnames THEN  -- keep going
   ELSE RETURN NEW;  -- exit, nothing to do
   END IF;
END IF;

IF TG_OP IN ('DELETE', 'UPDATE') THEN
   DELETE FROM hostname h
   USING  unnest(OLD.hostnames) d(x)
   WHERE  h.hostname = d.x;

   IF TG_OP = 'DELETE' THEN RETURN OLD;  -- exit, we are done
   END IF;
END IF;

-- control only reaches here for INSERT or UPDATE (with actual changes)
INSERT INTO hostname(hostname)
SELECT h
FROM   unnest(NEW.hostnames) h;

RETURN NEW;
END
$func$ LANGUAGE plpgsql;

Триггер:

CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();

SQL Fiddle с тестовым прогоном.

Для работы с ним используйте индекс GIN для операторов массива host.hostnames и :

23
ответ дан Community 26 August 2018 в 11:47
поделиться

Если кому-то все еще нужно то, что было в исходном вопросе:

CREATE TABLE testtable(
    id serial PRIMARY KEY,
    refs integer[],
    EXCLUDE USING gist( refs WITH && )
);

INSERT INTO testtable( refs ) VALUES( ARRAY[100,200] );
INSERT INTO testtable( refs ) VALUES( ARRAY[200,300] );

, и это даст вам:

ERROR:  conflicting key value violates exclusion constraint "testtable_refs_excl"
DETAIL:  Key (refs)=({200,300}) conflicts with existing key (refs)=({100,200}).

Проверено в Postgres 9.5 на Windows.

Обратите внимание, что это создало бы индекс с помощью оператора &&. Поэтому, когда вы работаете с testtable, было бы быстрее проверить ARRAY[x] && refs, чем x = ANY( refs ) из-за индексирования внутренних объектов Postgres.

P.S. Как правило, я согласен с вышеприведенным ответом, но этот подход - просто приятный вариант, когда вам не нужно действительно беспокоиться о производительности и т. Д.

5
ответ дан volvpavl 26 August 2018 в 11:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: