Праведният път
Може да искате да преразгледате нормализирането вашата схема. Не е необходимо всеки да се „присъединява дори за най-простата заявка“ . Създайте 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+
Започвайки от стр. 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';
В Postgres 9.2+ индекс с много колони би бил още по-добър, ако можете да получите сканиране само за индекс от това:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Започвайки с Postgres 9.3 , можете да използвате MATERIALIZED VIEW
, позволяващи обстоятелствата. Особено ако четете много по-често, отколкото пишете на масата.
Тъмната страна (това, което всъщност попитахте)
Ако не мога да те убедя в праведния път, ще помогна и на тъмната страна. аз съм гъвкав. :)
Ето демонстрация как да наложите уникалност на имената на хостове. Използвам таблица hostname
за събиране на имена на хостове и тригер в таблицата host
за да го поддържам актуален. Уникалните нарушения предизвикват изключение и прекратяват операцията.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Функция за задействане:
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
и оператори за масив за да работите с него:
- Защо моят PostgreSQL индекс на масива не се използва (Rails 4)?
- Проверете дали някоя от даден масив от стойности присъства в масив на Postgres