Модераторы: LSD

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Триггер и изменяющаяся таблица (mutating table), Триггер и изменяющаяся таблица 
:(
    Опции темы
Matrex
Дата 20.6.2011, 09:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 96
Регистрация: 31.1.2006

Репутация: нет
Всего: 2



Есть таблица prspc_content которая содержит иерархические данные:

ID - идентификатор
FATHER - родитель
COUNT - количество
MULTI – множитель

Пытаюсь написать триггер BEFOR UPDATE для этой таблицы, суть которого – при изменении поля COUNT – пересчитать поле MULTI для всех подчиненных из этой же таблицы, например:

ID      FATHER  COUNT   MULTI
1         0           10          1            
2         1           5            0
3         1           4            0

Изменяя запись с ID=1 (COUNT=10) на COUNT=5 пересчитать значения поля MULTI для записей с ID=2 и ID=3. Как правильно это сделать, что бы не вылетала ошибка «table is mutating»? Задача, казалось бы, классическая, но за отсутствием опыта я в тупике.

Вот как делаю я:

Код

create or replace trigger CALCMULTIUPDATE
  before update on prspc_content  
  for each row
declare 

cursor c_mod is select t.* from prspc_content t where id<>:new.id connect by prior t.id=t.father start With t.id=:new.id;

begin

 for v_dat in c_mod loop
    update prspc_content i set i.multi=production.GET_MULTI(v_dat.id) where i.id=v_dat.id;
 end loop;   
 
end CALCMULTIUPDATE;


А как правильно?


PM MAIL   Вверх
turbanoff
Дата 22.6.2011, 10:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.4.2009

Репутация: нет
Всего: 1



а поясните, пожалуйста, суть полей COUNT и MULTI
Обычно, мутация таблиц - следствие неверного проектирования
PM MAIL   Вверх
Matrex
Дата 22.6.2011, 16:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 96
Регистрация: 31.1.2006

Репутация: нет
Всего: 2



COUNT – количество деталей в единице изделия
MULTI – множитель (с учетом входимости)

Но это не имеет принципиального значения. Я так понимаю, проблема здесь в том, что я пытаюсь изменить ту же самую таблицу, для которой сработал триггер. Поэтому триггер вызывается рекурсивно, а это, судя по всему, БД переварить не может. Сейчас этот алгоритм реализован программно, т.е. в exe-шнике – все работает – просто хочется немного оптимизировать задачу и часть нагрузки повесить на сервак…


Это сообщение отредактировал(а) Matrex - 22.6.2011, 16:59
PM MAIL   Вверх
turbanoff
Дата 23.6.2011, 06:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.4.2009

Репутация: нет
Всего: 1



Подход к БД не правильный - при изменении записи менять другие. Значит схема не нормализована.
может быть поле возможно MULTI высчитывать при запросе?
Можно отдельную вьюху завести если уже много кода завязано (или вирт. столбец в 11-м оракле)

Если все же решите остановиться на своем варианте - можно использовать планировщик Oracle.
В триггере создаете задание, которое  будет выполнено 1 раз. Если используете 10+ Oracle - используется пакет DBMS_SHEDULER, если 9- - DBMS_JOB (хотя его можно и в более поздних версиях использовать).
Но тут есть подводный камень - даже если указать, что задание необходимо выполнить немедленно - все равно будет задержка.

Это сообщение отредактировал(а) turbanoff - 23.6.2011, 06:56
PM MAIL   Вверх
turbanoff
Дата 23.6.2011, 07:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.4.2009

Репутация: нет
Всего: 1



есть еще общий поход:
придется создать 3 триггера:
1. before общий (не for each row)
2. before for each row
3. after общий

создается пакет с переменной - pl/sql таблицей.

в 1-м триггере - переменная (таблица) очищается
во 2-м - в таблицу записывается id записей для которых необходимо выполнить пересчет
в 3-м (after) выполняется пересчет таблицы. по тем ID которые запомнены в переменной пакета.


PS. Если это единственный триггер - никакой рекурсии вызова триггера не происходит: триггер на апдейт prspc_content , а в триггере меняется другое поле - multi. 
Мутация  - выборка или изменение таблицы A в триггере for each row. при этом триггер сработал в результате изменения таблицы A.

В книге Oracle PL/SQL для профессионалов. Практические решения - Коннор МакДональд есть глава посвященная мутации таблиц.

Это сообщение отредактировал(а) turbanoff - 23.6.2011, 07:21
PM MAIL   Вверх
Matrex
Дата 23.6.2011, 08:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 96
Регистрация: 31.1.2006

Репутация: нет
Всего: 2



Цитата

Подход к БД не правильный - при изменении записи менять другие.


Ну не знаю… Может быть, и найдется другое решение – буду рад услышать. Тогда попробую объяснить, зачем это нужно.

Собственно подобной таблицей (prspc_content) описывается состав изделия:

ID      FATHER  COUNT   MULTI   ID_DSE                  ID_PRSPC
1         0               1      1         100 (Стол)              200   (Тумба)
2         1               5      1         101 (Винт)              200   (Тумба)
3         1               4      1         102 (Шайба)           200   (Тумба)

т.е. есть изделие «Тумба» которое состоит из одного «Стола» (COUNT=1), который в свою очередь состоит из 5 «Винтов» и 4 «Шайб». Задача – посчитать количество деталей на изготовление одной тумбы. Тут все просто. Можно посчитать хоть на сто тумб. Теперь изменим ситуацию – представим, что тумба состоит из двух столов (количество «Столов» COUNT = 2, при этом количество «Винтов» и «Шайб» не меняем /требование заказчика/) тогда получается так – что на изготовление одной тумбы требуется 2 стола 10 винтов и 8 шайб. 

Для того, что бы правильно рассчитать количество деталей, необходим предварительный расчет некоего «множителя». Раньше он рассчитывался динамически во время выполнения запроса – просто вызывалась функция для каждого идентификатора детали. Но когда составы изделий выросли до 200-300 единиц, а номенклатура до 40-50 наименований, да плюс неограниченное количество «вложенности» – возникли проблемы с производительностью.

Цитата

может быть поле возможно MULTI высчитывать при запросе?


При добавлении записей это именно так и делается. А вот что делать если запись меняется. См. пример выше…

Цитата

есть еще общий поход:
придется создать 3 триггера:


Ясно буду пробовать. Спасибо…

PM MAIL   Вверх
turbanoff
Дата 23.6.2011, 11:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.4.2009

Репутация: нет
Всего: 1



насколько я понял:
MULTI - это собственно количество деталей в изделии.
а count - это не количество деталей, а кол-во наборов поддеталей.

правильно?

Теперь осталось понять когда необходимо пересчитывать multi, и зачем?
PM MAIL   Вверх
Matrex
Дата 23.6.2011, 13:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 96
Регистрация: 31.1.2006

Репутация: нет
Всего: 2



Цитата

MULTI - это собственно количество деталей в изделии.

Нет. Это множитель для пересчета фактического количества деталей с учетом входимости

Цитата

а count - это не количество деталей, а кол-во наборов поддеталей.

Нет. Это количество деталей в изделии


Например. Вот две спецификации:

НАИМЕНОВАНИЕ ДЕТАЛИ    КОЛИЧЕСТВО (COUNT)
Тумба                                1
  Стол                                1  < тумба состоит из одного стола
      Винт                            5  < для изготовления одного стола требуется 5 винтов
      Шайба                         4  < и 4 шайбы
Всего деталей для изготовления такой тумбы: 1 стол; 5 винтов, 4 шайбы.

и

НАИМЕНОВАНИЕ ДЕТАЛИ    КОЛИЧЕСТВО (COUNT)
Тумба                                1
  Стол                                2  <<<<<< тумба состоит из ДВУХ столов
      Винт                            5  < для изготовления ОДНОГО стола требуется 5 винтов
      Шайба                         4  < и 4 шайбы
Всего деталей для изготовления такой тумбы:  2 стола; 10 винтов, 8 шайб.

При составлении спецификаций количество деталей указывается на 1 сборочную единицу см. количество «Столов» (2 шт) и количество «Винтов» и «Шайб» (указано на изготовление одного стола).

Цитата

Теперь осталось понять когда необходимо пересчитывать multi, и зачем?


Пересчет должен выполняться при добавлении новой записи и при изменении количества деталей родительского элемента для всех его подчиненных. А зачем? Что бы корректно посчитать количество деталей необходимых для изготовления изделия (расчет выполняется путем перемножения COUNT*MULTI).

Собственно говоря, это все. По другому как это сделать я не знаю. Если есть соображения – я бы послушал…

PM MAIL   Вверх
turbanoff
Дата 23.6.2011, 13:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.4.2009

Репутация: нет
Всего: 1



ааа, вот оно как. то есть multi введено только ради производительности?.
А можно узнать сколько строк в таблице? хотя бы порядок. Oracle может "прожевывать" довольно большие таблицы быстро, если есть индексы.

Это сообщение отредактировал(а) turbanoff - 23.6.2011, 13:27
PM MAIL   Вверх
DimW
Дата 23.6.2011, 13:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1330
Регистрация: 24.2.2005
Где: Орёл

Репутация: 23
Всего: 44



давно уже ридонли, но иногда нервы не выдерживают.

pragma autonomous_transaction для триггера.
PM MAIL ICQ   Вверх
Matrex
Дата 23.6.2011, 14:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 96
Регистрация: 31.1.2006

Репутация: нет
Всего: 2



Цитата

ааа, вот оно как. то есть multi введено только ради производительности?.

Да.

Цитата

А можно узнать сколько строк в таблице? хотя бы порядок. Oracle может "прожевывать" довольно большие таблицы быстро, если есть индексы.


40 изделий x 300 деталей т.е. обрабатывается около 12000 записей для расчета необходимого количества деталей. Если для каждой из записи во время selecta рассчитывать множитель по функции – по времени занимает около 3х минут. И индексы тут не помогают т.к. для каждой из 12000 записей необходимо выполнить иерархический select для расчета множителя, а вот если множитель рассчитан заранее - около 1 сек…

Цитата

давно уже ридонли, но иногда нервы не выдерживают.

А чего так?

Цитата

pragma autonomous_transaction для триггера.
 
Понял – буду читать матчасть. Спасибо.

PM MAIL   Вверх
LSD
Дата 23.6.2011, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 18
Всего: 538



Цитата(DimW @  23.6.2011,  14:27 Найти цитируемый пост)
pragma autonomous_transaction для триггера

Дык он текущих изменений не увидит, как же ему пересчитывать данные?




Цитата(turbanoff @  23.6.2011,  07:55 Найти цитируемый пост)
Но тут есть подводный камень - даже если указать, что задание необходимо выполнить немедленно - все равно будет задержка.

Подводный камень как раз в обратном, JOB может сработать раньше, чем закончится транзакция и JOB просто не увидит изменений smile

Добавлено через 5 минут и 22 секунды
"По правилам" в таблицах не стоит хранить данные, которые можно вычислить на основе уже имеющихся данных.

Мне кажется стоит сделать или view, или materialized view если будут проблемы с производительностью.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Matrex
Дата 23.6.2011, 14:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 96
Регистрация: 31.1.2006

Репутация: нет
Всего: 2



Если сделать вьюшку, то все равно придется вызывать функцию расчета множителя – а это сведет на нет весь эффект от повышения производительности (у меня так и было в начале, другое дело что может функцию расчета множителя как то оптимизировать…). В данном конкретном случае метод «по правилам» не очень подходит. ИМХО, проще написать два триггера на добавление/изменение записи... А тут такая засада... На добавление триггер работает – тут все ок, а вот с изменением…

Это сообщение отредактировал(а) Matrex - 23.6.2011, 14:52
PM MAIL   Вверх
DimW
Дата 23.6.2011, 15:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1330
Регистрация: 24.2.2005
Где: Орёл

Репутация: 23
Всего: 44



Цитата(LSD @  23.6.2011,  14:17 Найти цитируемый пост)
Дык он текущих изменений не увидит, как же ему пересчитывать данные?



LSD, какие текущие изменения нужны?
Код

SQL> create table test_trg
  2  (id number
  3  ,cnt number)
  4  /
 
Table created
SQL> create or replace trigger test_trg_bu
  2    before update on test_trg
  3    for each row
  4  declare
  5    pragma autonomous_transaction;
  6  begin
  7   raise_application_error(-20001, '***' || :new.cnt || '***');
  8  end;
  9  /
 
Trigger created
SQL> insert into test_trg values(10, 100)
  2  /
 
1 row inserted
SQL> update test_trg
  2     set cnt = 555
  3   where id = 10
  4  /
 
update test_trg
   set cnt = 555
 where id = 10
 
ORA-20001: ***555***
ORA-06512: на  "TEST_TRG_BU", line 4
ORA-04088: ошибка во время выполнения триггера 'TEST_TRG_BU'


Добавлено через 4 минуты и 2 секунды
Цитата(Matrex @  23.6.2011,  14:51 Найти цитируемый пост)
а вот с изменением…

опять... ты что мат часть изучил уже?

Добавлено через 9 минут и 25 секунд
Цитата(Matrex @  23.6.2011,  14:03 Найти цитируемый пост)
А чего так?

время жизни постов обратно пропорционально моему свободному времени, вот как сейчас, сказал слово и началось  smile 

Это сообщение отредактировал(а) DimW - 23.6.2011, 15:26
PM MAIL ICQ   Вверх
Matrex
Дата 23.6.2011, 15:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 96
Регистрация: 31.1.2006

Репутация: нет
Всего: 2



Всем спасибо, особый респект DimW вот решение:

Код

create or replace trigger CALCMULTIUPDATE
  before update on prspc_content  
  for each row
declare 
   pragma autonomous_transaction;
   cursor c_mod is select t.* from prspc_content t where id<>:new.id connect by prior t.id=t.father start With t.id=:new.id;
   n integer;
begin

 for v_dat in c_mod loop
    n:=production.GET_MULTI(v_dat.id);
    update prspc_content i set i.multi=n where i.id=v_dat.id;
 end loop;
 commit;   
 
end CALCMULTIUPDATE;


PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Oracle"
Zloxa
LSD

Данный раздел предназначен для обсуждения проблем с Oracle Database, другие продукты Oracle здесь не обсуждаются. Просьба при создании темы, придерживаться следующих правил:

  • при создании темы давайте ей осмысленное название, описывающее суть проблемы
  • указывайте используемую версию базы, способ соединения и язык программирования
  • при ошибках обязательно приводите код ошибки и сообщение сервера
  • приводите код в котором возникла ошибка, по возможности дайте тестовый пример демонстрирующий ошибку
  • при вставке кода используйте соответсвующие теги: [code=sql] [/code] для подсветки SQL и PL/SQL кода, [code=java] [/code] - для Java, и т.д.

  • документация по Oracle: 9i, 10g, 11g
  • книги по Oracle можно поискать здесь
  • действия модераторов можно обсудить здесь

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Zloxa, LSD.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Oracle | Следующая тема »


 




[ Время генерации скрипта: 0.1343 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.