2016-07-21 2 views
0

Я создал 2 стола: INFORMATION И FEED.Как разрешить столбцу значения автоматически отражать, когда значение вставлено/обновлено/удалено в/из другого столбца в той же таблице?

INFORMATION has 2 attributes : ID(Primary Key), TOT_AMOUNT. 

FEED has 4 attributes : ID(Foreign key refer INFORMATION(ID)), S_AMOUNT, S_DATE, TOT_REM. 

Теперь я должен вставить/обновить/удалить значения в/из TOT_REM, на основе ввода/удаления/обновления S_AMOUNT и TOT_AMOUNT.

Выборочные содержание:

INFORMATION Table 
------------------ 
ID | TOT_AMOUNT 
1 | 100 
2 | 20 
3 | 50 
... 

       FEED Table 
---------------------------------------- 
ID | S_AMOUNT | S_DATE | TOT_REM 
1 |  10  |10.10.2010| 90 
1 |  10  |13.10.2010| 80 
1 |  30  |17.10.2013| 50 
1 |  10  |20.10.2016| 40 
... 

Нам нужно автоматически вставить значение в TOT_REM атрибут, на основе обновления/вставки/удаления операции, выполняемые на S_AMOUNT, с помощью TOT_AMOUNT & S_AMOUNT.

В любое время, TOT_REM не может быть меньше 0. И, TOT_REM должен быть автоматически вставлен/удалены/обновлены таким образом, что

TOT_REM for i(at a specific date) = (TOT_AMOUNT for ID=i) - 
            SUM(S_AMOUNT of all instances of ID=i, 
            which is later than the S_DATE for ID=i); 

Таким образом, при условии, если мы удаляем 2-й кортеж (1, 10, '13 .10.2010' , 80), отраженное состояние BR_FEED должно быть:

   FEED Table 
---------------------------------------- 
ID | S_AMOUNT | S_DATE | TOT_REM 
1 |  10  |10.10.2010| 90 
1 |  30  |17.10.2013| 60 
1 |  10  |20.10.2016| 50 
... 

я написал триггер, который терпит неудачу, показывающий

ORA-04091: table SSUMAN.FEED is mutating, trigger/function may not see it 

Кода триггера:

CREATE OR REPLACE TRIGGER BR_INSERT_TRB 
AFTER DELETE OR INSERT OR UPDATE OF S_AMOUNT ON FEED 
FOR EACH ROW 
BEGIN 

IF DELETING THEN 
UPDATE FEED bf 
SET  bf.TOT_REM = bf.S_AMOUNT + :OLD.S_AMOUNT; 
END IF; 

IF INSERTING THEN 
INSERT INTO FEED (TOT_REM) VALUES(
((SELECT TOT_AMOUNT FROM INFORMATION bi WHERE bi.ID=:NEW.ID) - 
(SELECT SUM(S_AMOUNT) FROM FEED bf where bf.ID=:NEW.ID) - 
:NEW.S_AMOUNT); 
END IF; 

IF UPDATING THEN 
UPDATE FEED bf 
SET  bf.TOT_REM = (SELECT TOT_AMOUNT FROM BR_INFORMATION bi WHERE bi.ID=bf.ID) - 
(SELECT SUM(S_AMOUNT) FROM FEED bf where bf.ID=:NEW.ID) - 
:NEW.S_AMOUNT 
WHERE :NEW.ID IS NOT NULL; 
END IF; 

END; 

Вопросы:

  1. испорчен ли такой подход? Не могу ли я достичь того, чего хочу, таким образом? [ДОПОЛНИТЕЛЬНО]
  2. Есть ли какие-либо возможности для просмотра? Я не могу думать в этой строке! Возможно, отсутствие опыта ... [НЕОБЯЗАТЕЛЬНО]
  3. Любой лучший подход, так что значения TOT_REM могут быть автоматически отражены? [ОБЯЗАТЕЛЬНО ОТВЕТИТЬ]
+0

Когда вы говорите: «TOT_REM не может быть меньше 0» - вы имеете в виду это как часть логики вычисления TOT_REM, или если нарушающая вставка/обновление/удаление будет ОТКАЗЫВАЕТСЯ, потому что это приведет к тому, что TOT_REM станет отрицательным? Они совершенно разные и решаются по-разному. – mathguy

+0

@mathguy - Он должен быть отклонен, спасибо, что попросил разъяснений. Требуется любая другая деталь? –

+0

Я вижу здесь несколько трудностей. Самый большой - это вычисление TOT_REM в начале. Если вы удалите строку 10.10.2010, то TOT_REM в следующей строке необходимо прочитать из таблицы INFORMATION, а не из строки над ней. Если вы добавите строку выше первой (выше 10.10.2010), TOT_ROM 10.10.2010 нужно будет рассчитывать на основе строки над ней, а не из ИНФОРМАЦИИ. Еще большая проблема: вы отклоняете строку, потому что это сделает TOT_ROM отрицательным. В более позднее время вы удалите строку со старой датой ... теперь вам нужно вернуть обратно строку, которую вы отклонили вначале? – mathguy

ответ

1

Я думаю, что лучше создать вид. Посмотрите на это

Тестовые данные

create table feed(ID,S_AMOUNT,S_DATE) as (
    SELECT 1,10, TO_DATE('10.10.2010','dd.mm.yyyy') FROM dual UNION all 
    SELECT 1,10,TO_DATE('13.10.2010','dd.mm.yyyy') FROM dual UNION all 
    SELECT 1,30,TO_DATE('17.10.2013','dd.mm.yyyy') FROM dual UNION all 
    SELECT 2,10,TO_DATE('20.10.2016','dd.mm.yyyy') FROM dual) 

create table INFORMATION (id, TOT_AMOUNT) as (
    SELECT 1,100 FROM DUAL UNION ALL 
    SELECT 2,20 FROM DUAL UNION ALL 
    SELECT 3,50 FROM DUAL) 

Запрос

create or replace view result_feed as 
    SELECT f.*,i.TOT_AMOUNT - NVL(SUM(S_AMOUNT) OVER(PARTITION BY f.ID ORDER BY f.S_DATE),0) AS tot_rem FROM FEED f, INFORMATION i 
    WHERE f.ID = i.id 
    ORDER BY f.ID, f.S_DATE; 
    -- used NVL to prevent side-effect of null values 


    SELECT * from RESULT_FEED; 

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

+0

Спасибо, это оказалось полезным и решило мою проблему. Любая причина, по которой не использовать виртуальный столбец и предпочитать просмотр? –

+0

@Am_I_Helpful В этом случае я не использую виртуальный столбец, потому что в нем запрещено использовать аналитическую функцию (я не знаю, как реализовать вашу логику с отсутствием аналитических функций). В будущем: я буду использовать виртуальный столбец, если он используется как предикат в некотором сложном запросе или используется как ограничение или для какой-то цели мне нужен индекс. Некоторым разработчикам не нравятся представления, потому что, как они говорят, их бизнес имеет сложную логику и лучше работает с таблицей вместо представления. Я использовал представление вообще как результат некоторого отчета или некоторой абстракции данных пользователя. –

1

Если бы это был мой бизнес проблема, и я мог бы начать с нуля (что не может быть возможно в вашем случае), я бы держать INFORMATION стол, как есть, я хотел бы удалить TOT_REM столбец из FEED, и я бы создайте представление, которое выглядит как текущая таблица FEED.Вы можете написать всю необходимую логику в определении представления.

ДОБАВЛЕНО:

Во-первых, здесь является определение представления; он принимает базовые таблицы INFORMATION и FEED, как описано ОП, без столбца TOT_REM в FEED.

create view remaining_balance (id, s_amount, s_date, tot_rem) as 
select i.id, f.s_amount, f.s_date, 
     i.tot_amount - nvl(sum(f.s_amount) over (partition by f.id order by f.s_date), 0) 
from information i left outer join feed f 
        on i.id = f.id 
; 

Вид использует внешнее соединение, чтобы включить id «S из INFORMATION таблицы, которые не имеют каких-либо соответствующие строки в FEED. (Тогда, чтобы иметь дело с нулями в вычислении TOT_REM, я использую функцию nvl() для преобразования NULL в 0.)

Вот пример запуска вид:

SQL> select * from information; 

     ID TOT_AMOUNT 
---------- ---------- 
     1  100 
     2   20 
     3   50 

3 rows selected. 

SQL> select * from feed; 

     ID S_AMOUNT S_DATE 
---------- ---------- ---------- 
     1   10 2010-10-10 
     1   10 2010-10-13 
     1   30 2010-10-17 
     1   10 2016-10-20 

4 rows selected. 

SQL> select * from remaining_balance order by id, s_date; 

     ID S_AMOUNT S_DATE  TOT_REM 
---------- ---------- ---------- ---------- 
     1   10 2010-10-10   90 
     1   10 2010-10-13   80 
     1   30 2010-10-17   50 
     1   10 2016-10-20   40 
     2        20 
     3        50 

6 rows selected. 

Теперь Грамотно установленный метод для обеспечения сложных ограничений заключается в использовании материализованных представлений. Ограничения прямой проверки работают только на уровне строк и не могут использоваться, когда условия содержат более одной таблицы. В текущей задаче проверка выполняется против двух таблиц, а TOT_REM зависит от других строк таблицы FEED - поэтому ограничение в таблице FEED не будет работать.

Материализованный подход к представлению заключается в том, чтобы определить вид, подобный тому, который я создал, в качестве материализованного представления, для определения его с помощью refresh fast on commit (так что ограничения проверяются сразу после операций DML в базовых таблицах) и для создания проверьте ограничение на материализованное представление. В данной проблеме это будет проверка на TOT_RM >= 0.

Увы, refresh fast on commit запрещен (по крайней мере, так же, как Oracle версии 11.2, что у меня есть), когда определение представления использует аналитические функции. Я использовал аналитическую версию функции sum(), поэтому это не сработает.

кажется, имеет смысл, однако, определить различные материализованное представление, как показано ниже:

create materialized view remaining_balance (id, tot_rem) as 
select i.id, i.tot_amount - f.sum_s_amount 
from information i inner join (select id, sum(s_amount) as sum_s_amount 
           from  feed 
           group by id) f 
        on i.id = f.id 
; 

SQL> select * from remaining_balance; 

     ID TOT_REM 
---------- ---------- 
     1   40 

Я не использовать внешнее соединение больше, так как это только предполагается показать оставшиеся остатки. Я полагаю, что INFORMATION имеет контрольное ограничение на TOT_AMOUNT, чтобы убедиться, что оно неотрицательно, и id в FEED является первичным ключом, указывающим на id в INFORMATION, поэтому нет дополнительной информации, открытой внешним соединением для этой версии представления. (Однако, при желании, можно включить все id).

Здесь вы должны быть в форме представления с помощью refresh fast on commit и для добавления проверки к действию tot_rem >= 0. Увы, я не могу это проверить; расширенная репликация (требуется для создания материализованных журналов просмотра, которые в свою очередь необходимы для refresh fast) недоступна/включена в бесплатной экспресс-версии Oracle, которую я имею. Попытайтесь поэкспериментировать с этим, хотя это может быть нужное вам решение. Удачи!

+0

Можете ли вы расширить определение вида (код)? Я не смог разработать определение для представления. –

+0

Я могу, но это не поможет, если вам действительно нужно ограничение, а не только решение для отчетности. – mathguy

+0

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

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