Автоматически обновлять поле в базе данных - PullRequest
1 голос
/ 22 сентября 2009

Я хочу автоматически обновить столбец базы данных с помощью совокупности другого столбца. Здесь задействованы три таблицы:

T_RIDER
  RIDER_ID
  TMP_PONYLIST
  ...

T_RIDER_PONY
  RIDER_ID
  PONY_ID

T_PONY
  PONY_ID
  PONY_NAME
  ...

T_RIDER и T_PONY имеют отношение n: m через 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, так в чем здесь проблема?

UPDATE Ответы Винсента и Тониса были разные, но оба полезны. Я принял Тониса, но обязательно прочитайте и ответ Винсента.

Ответы [ 2 ]

3 голосов
/ 22 сентября 2009

информация в столбце 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;

См. этот SO для примера агрегирования строк до 11gR2.

1 голос
/ 22 сентября 2009

Зачем вам нужно читать таблицу 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, чтобы иметь дело с этим! Подробнее см. этот SO-ответ .

...