2017-01-05 7 views
1

Ich habe eine Tabelle mit vielen Spalten und mehrere Millionen Zeilen wieUp-to-date Wörterbuch der unterschiedlichen Werte für Spalte

CREATE TABLE foo (
id integer, 
thing1 text, 
thing2 text, 
... 
stuff text); 

Wie kann ich die Relevanz von Wörterbuch der eindeutigen Werte von stuff Spalte verwalten, die ursprünglich bevölkert dies wie:

INSERT INTO stuff_dict SELECT DISTINCT stuff from foo; 

Soll ich (überprüfen, ob neue stuff Wert bereits in stuff_dict vor jedem insert/update) oder Verwendung löst für jedes insert/update/delete aus foo Tabelle manuell synchronisieren. Was ist in letzterem Fall das beste Design für einen solchen Trigger?

UPDATE: Ansicht passt hier nicht, weil die SELECT * FROM stuff_dict sollte so schnell wie möglich laufen (auch CREATE INDEX ON foo(stuff) hilft nicht viel, wenn foo hat Dutzende von Millionen von Datensätzen).

Antwort

3

Eine materialisierte Ansicht scheint die einfachste Möglichkeit für einen großen Tisch zu sein.

In der Trigger-Funktion aktualisiert nur die Ansicht. Sie können die Option concurrently verwenden (siehe den Kommentar der pozs unten).

create materialized view stuff_dict as 
    select distinct stuff 
    from foo; 

create or replace function refresh_stuff_dict() 
returns trigger language plpgsql 
as $$ 
begin 
    refresh materialized view /*concurrently*/ stuff_dict; 
    return null; 
end $$; 

create trigger refresh_stuff_dict 
after insert or update or delete or truncate 
on foo for each statement 
execute procedure refresh_stuff_dict(); 

Während die Lösung mit einer materialisierten Ansicht einfach ist es nicht optimal sein kann, wenn die Tabelle foo häufig geändert wird. Verwenden Sie in diesem Fall eine Tabelle für das Wörterbuch. Ein Index wird hilfreich sein.

create table stuff_dict as 
    select distinct stuff 
    from foo; 

create index on stuff_dict(stuff); 

Die Triggerfunktion ist komplizierter und sollte für jede Zeile nach insert/update/delete abgefeuert werden:

create or replace function refresh_stuff_dict() 
returns trigger language plpgsql 
as $$ 
declare 
    do_insert boolean := tg_op = 'INSERT' or tg_op = 'UPDATE' and new.stuff <> old.stuff; 
    do_delete boolean := tg_op = 'DELETE' or tg_op = 'UPDATE' and new.stuff <> old.stuff; 
begin 
    if do_insert and not exists (select 1 from stuff_dict where stuff = new.stuff) then 
     insert into stuff_dict values(new.stuff); 
    end if; 
    if do_delete and not exists (select 1 from foo where stuff = old.stuff) then 
     delete from stuff_dict 
     where stuff = old.stuff; 
    end if; 
    return case tg_op when 'DELETE' then old else new end; 
end $$; 

create trigger refresh_stuff_dict 
after insert or update or delete 
on foo for each row 
execute procedure refresh_stuff_dict(); 
+0

Trotz seines Namens 'Refresh concurrently' materialisierte Ansicht wird die Ausführung blockiert, bis es fertig ist (Die Lesevorgänge anderer Sitzungen werden erst blockiert, wenn die Aktualisierung abgeschlossen ist). Es ist tatsächlich langsamer als ohne 'gleichzeitige' auf größeren Datensätzen. - Aber ja, materialisierte Sichten werden für den OP-Anwendungsfall erstellt. – pozs

+0

@pozs - Leider hatte ich nie Gelegenheit, diese Option in der Praxis zu nutzen. Der Begriff ist in der Tat irreführend. Danke für die Korrektur. – klin

Verwandte Themen