2009-09-22 2 views
1

Я хотел бы автоматически обновить столбец базы данных агрегированием другого столбца. Есть три таблицы, связанные:Автоматическое обновление поля в базе данных

T_RIDER 
    RIDER_ID 
    TMP_PONYLIST 
    ... 

T_RIDER_PONY 
    RIDER_ID 
    PONY_ID 

T_PONY 
    PONY_ID 
    PONY_NAME 
    ... 

T_RIDER и T_PONY имеющие п: отношения м через T_RIDER_PONY. T_RIDER и T_PONY есть еще несколько столбцов, но только TMP_PONYLIST и PONY_NAME актуальны.

TMP_PONYLIST является точкой с запятой, указанным в таблице PONY_NAMES, представьте себе что-то вроде "Twisty Tail;Candy Cane;Lucky Leaf". Я хотел бы сохранить это поле в актуальном состоянии, что бы ни случилось с T_RIDER_PONY или T_PONY.

Моя первая идея состояла в том, чтобы иметь триггеры на T_RIDER_PONY и T_PONY. Проблема в том, что в триггере невозможно читать T_RIDER_PONY, я всегда получаю ORA-04091. Я нашел несколько советов о работе с тремя триггерами и переменными пакета, но это звучит слишком сложно.

Возможно, вы думаете, что мне лучше изменить схему или полностью избавиться от TMP_PONYLIST. Это варианты, но не тема этого вопроса. На данный момент Меня интересуют только ответы, которые не требуют каких-либо изменений в моих приложениях. (ни одно приложение не работает непосредственно с таблицами, только виды, поэтому допускаются обманки с представлениями).

Итак, как я могу автоматически обновлять TMP_PONYLIST? Как конкатенировать строку - интересная подзадача, я еще не нашел элегантного решения.

Я использую Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi.

UPDATE

Мне нравится идея использования материализованного представления. То, что я это:

CREATE MATERIALIZED VIEW 
    V_TMP_PONYLIST 
BUILD IMMEDIATE 
REFRESH COMPLETE ON COMMIT 
AS SELECT 
    R.RIDER_ID, string_agg(P.PONY_NAME) AS TMP_PONYLIST 
FROM 
    T_PONY P, T_RIDER R, T_RIDER_PONY RP 
WHERE 
    P.PLOTGROUP_ID=RP.PLOTGROUP_ID AND 
    R.QUEUE_ID=RP.QUEUE_ID 
GROUP BY R.RIDER_ID; 

string_agg не показывается, потому что это долго, и я думаю, что это не имеет отношения.

Он не компилируется с ON COMMIT, я получаю ORA-12054. Насколько я понимаю, агрегаторы документации запрещены только с REFRESH FAST, так что в чем проблема?

ОБНОВЛЕНИЕ Ответы Vincents и Tonys были разными, но полезны. Я принял Тониса, но обязательно прочитайте также ответ Винсента.

+1

Я бы посоветовал не хранить несколько значений («Twisty Tail; Candy Cane, Lucky Leaf») в одном столбце ... –

ответ

1

Зачем вам нужно прочитать таблицу T_RIDER_PONY в триггере?У вас будет либо вставить или удалить строку, так что все триггер нужно сделать, это найти имя конского в T_PONY и обновление таблицы T_RIDER, либо прибавление имени или его удаление из TMP_PONYLIST как этот

create trigger t_rider_pony_trg 
after insert or delete on t_rider_pony 
for each row 
begin 
    if inserting then 
     select pony_name 
     into l_pony_name 
     where pony_id = :new.pony_id; 

     update t_rider 
     set tmp_ponylist = tmp_ponylist || ';' || l_pony_name 
     where rider_id = :new.rider_id; 
    elsif deleting then 
     select pony_name 
     into l_pony_name 
     where pony_id = :old.pony_id; 

     update t_rider 
     set tmp_ponylist = ltrim ( 
           rtrim ( 
            replace(';' || tmp_ponylist || ';', 
              ';' || l_pony_name || ';', 
              ';'), 
            ';', ';') 
     where rider_id = :old.rider_id; 
    end if; 
end; 

Это обновление в разделе удаления довольно неприятно, я допускаю; может быть предпочтительнее использовать утилиты, такие как apex_util.string_to_table и apex_util.table_to_string, чтобы справиться с этим! См. this SO answer.

+0

+1 Крутое решение, я думал, что манипуляции с строк были слишком сложными, но ваше решение действительно приятно , В настоящий момент tmp_ponylist сортируется. Считаете ли вы, что будет сложно расширить ваше решение, чтобы он сортировал tmp_ponylist? – bbuser

+0

Можно было бы отсортировать список, но я думаю, вам нужно будет использовать временную таблицу или пользовательский тип, чтобы сохранить ее и выбрать обратно по порядку. Или напишите себе некоторый код сортировки пузыря, чтобы работать с коллекцией PL/SQL! –

+0

Я не думаю, что это будет работать в многопользовательской среде. Пользователь A вставляет триггер обновления строки T_Rider; Пользователь B вставляет строку, не видит изменения А; Обновление блоков B; Зафиксирует, освобождает блокировку, обновления B; B коммиты, изменения А исчезли –

3

информация о столбце TMP_PONYLIST является избыточной (она существует в другом месте). Вы будете решать всевозможные проблемы для его поддержки (ни одно решение не будет работать корректно в многопользовательской среде, если не существует какой-либо механизм блокировки).

В нормализованной модели вы просто удаляете этот столбец из физической модели. Если вам нужна информация, вы можете использовать представление, например, с Oracle 11gR2:

CREATE OR REPLACE VIEW rider_v AS 
SELECT rider_id, /*...,*/ 
     (SELECT listagg(p.pony_name, ';') WITHIN GROUP (ORDER BY p.pony_name) 
      FROM t_pony p 
      JOIN t_rider_pony rp ON (p.pony_id = rp.pony_id) 
     WHERE rp.rider_id = r.rider_id) tmp_ponylist 
    FROM t_rider r; 

См this SO, например агрегатного строки перед 11gR2.

+0

TMP_PONYLIST был представлен по соображениям производительности. Мы можем избавиться от TMP_PONYLIST, но я все еще изучаю другие варианты. Мне нравится идея использования представления, но она должна быть материализована. См. Мое обновление вопроса. – bbuser

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