Sprawiedliwa ścieżka
Być może zechcesz ponownie rozważyć normalizację twój schemat. Nie wszyscy muszą „dołączyć do nawet najprostszego zapytania” . Utwórz VIEW
za to.
Tabela może wyglądać tak:
CREATE TABLE hostname (
hostname_id serial PRIMARY KEY
, host_id int REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname text UNIQUE
);
Zastępczy klucz podstawowy hostname_id
jest opcjonalny . Wolę mieć. W Twoim przypadku hostname
może być kluczem podstawowym. Ale wiele operacji jest szybszych dzięki prostej, małej integer
klucz. Utwórz ograniczenie klucza obcego, aby połączyć się z tabelą host
.
Utwórz taki widok:
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+
Począwszy od strony 9.1 , klucz podstawowy w GROUP BY
obejmuje wszystkie kolumny tej tabeli w SELECT
lista. Informacje o wydaniu wersji 9.1:
Zezwól na inne niż GROUP BY
kolumny na liście celów zapytania, gdy klucz podstawowy jest określony w GROUP BY
klauzula
Zapytania mogą używać widoku jak tabeli. Wyszukiwanie nazwy hosta będzie dużo szybciej w ten sposób:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
W Postgresie 9.2+ indeks wielokolumnowy byłby jeszcze lepszy, gdyby można było uzyskać skanowanie tylko do indeksu poza tym:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Począwszy od Postgresa 9.3 , możesz użyć MATERIALIZED VIEW
, okoliczności na to pozwalają. Zwłaszcza jeśli czytasz znacznie częściej niż piszesz do stołu.
Ciemna strona (o co właściwie pytałeś)
Jeśli nie mogę cię przekonać o prawej ścieżce, będę asystować również po ciemnej stronie. Jestem elastyczny. :)
Oto demo, jak wymusić unikalność nazw hostów. Używam tabeli hostname
do zbierania nazw hostów i wyzwalacza w tabeli host
aby go aktualizować. Unikalne naruszenia powodują wyjątek i przerywają operację.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Funkcja wyzwalania:
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;
Wyzwalacz:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
Skrzypce SQL z uruchomieniem testowym.
Użyj indeksu WZ w kolumnie tablicy host.hostnames
i operatory tablicowe pracować z nim:
- Dlaczego mój indeks tablicy PostgreSQL nie jest używany (Rails 4)?
- Sprawdź, czy w tablicy Postgres znajduje się jakakolwiek z podanej tablicy wartości