2017-01-05 6 views
1

У меня есть таблица с большим количеством столбцов и нескольких миллионов строк, какуточненного словаря различных значений для столбца

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

Как я могу управлять уместностью словаря уникальных значений stuff столбца, который первоначально заполняющими как это:

INSERT INTO stuff_dict SELECT DISTINCT stuff from foo; 

Должен ли я вручную синхронизировать (проверить, если новое значение stuff уже в stuff_dict перед каждой вставки/обновления) или использовать триггеры для каждой вставки/обновления/удаления из foo таблицы. В последнем случае, каков наилучший дизайн для такого триггера (ов)?

UPDATE: вид здесь не подходит, потому что SELECT * FROM stuff_dict должен работать как можно быстрее (даже CREATE INDEX ON foo(stuff) не помогает, когда foo имеет десятки миллионов записей).

ответ

3

Материализованный вид кажется самым простым вариантом для большой таблицы.

В функции триггера просто обновите представление. Вы можете использовать опцию concurrently (см. Комментарий pozs ниже).

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(); 

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

create table stuff_dict as 
    select distinct stuff 
    from foo; 

create index on stuff_dict(stuff); 

Функция триггера является более сложным и должен быть уволен для каждой строки после вставки/обновления/удаления:

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

Несмотря на свое название 'обновления материализованных представлений concurrently' будет блокировать выполнение до тех пор, пока не будет закончена (только чтение других сеансов не будет заблокировано до завершения обновления). Это на самом деле медленнее, чем без «одновременного» на больших наборах данных. - Но да, материализованные представления создаются для случая использования OP. – pozs

+0

@pozs - К сожалению, мне никогда не приходилось использовать этот вариант на практике. Этот термин действительно вводит в заблуждение. Спасибо за исправление. – klin

 Смежные вопросы

  • Нет связанных вопросов^_^