Чтобы обновить значения в таблице на основе состояния значений из другой таблицы - PullRequest
0 голосов
/ 20 февраля 2012

У меня есть две таблицы как под:

LEAVEENTITLE
Name                           Null     Type       
------------------------------ -------- -----------------------------------
CL_ENTITLED                    NOT NULL NUMBER(2)
APP_NO                                  NUMBER(6)                 
CL_USED                                 NUMBER(2)        
SL_ENTITLED                    NOT NULL NUMBER(2)        
SL_USED                                 NUMBER(2)        
LTA_ENTITLED                   NOT NULL NUMBER(2)        
LTA_USED                                NUMBER(2)        
ECN                            NOT NULL NUMBER(6)        
CL_UNUSED                               NUMBER(2)        
SL_UNUSED                               NUMBER(2)        
LTA_UNSUED                              NUMBER(2)  


LEAVES
Name                           Null     Type        
------------------------------ -------- ------------------------------------ 
APP_NO                         NOT NULL NUMBER(6)        
ECN                            NOT NULL NUMBER(6)        
FROM_DATE                      NOT NULL DATE
TO_DATE                        NOT NULL DATE        
APP_DATE                       NOT NULL DATE        
NO_OF_DAYS                     NOT NULL NUMBER(3)        
LEAVE_TYPE                     NOT NULL VARCHAR2(25)        
STATUS                         NOT NULL VARCHAR2(10)        
REMARK                                  VARCHAR2(100)

Я хочу обновить *_USED столбцы таблицы LEAVEENTITLE на основе значения LEAVE_TYPE в таблице LEAVES, например:

  • Если LEAVE_TYPE = 'SL', то я хочу обновить SL_USED=SL_USED + LEAVES.NO_OF_DAYS
  • Если LEAVE_TYPE = 'CL', то я хочу обновить CL_USED=CL_USED + LEAVES.NO_OF_DAYS

Ответы [ 2 ]

2 голосов
/ 20 февраля 2012

Не совсем ответ на вопрос, но слишком большой для комментария ...

Попытка сохранить сводные данные в родительской таблице обычно не очень хорошая идея, так как она подвержена ошибкам и путанице. Например, в этом случае вы хотите добавить значения от leaves к значениям _used в leaveentitle, что предполагает ненулевые значения _used, что, в свою очередь, означает, что вы намереваетесь запустить его более чем один раз. Но это означает, что вы можете считать одни и те же leaves данные более одного раза. Если я добавлю данные, как это:

insert into leaveentitle(app_no, ecn, cl_entitled, sl_entitled, lta_entitled)
values (1, 1234, 5, 10, 5);

insert into leaves (app_no, ecn, from_date, to_date, app_date,
    no_of_days, leave_type, status)
values (1, 1234, date '2012-01-01', date '2012-01-02', date '2011-12-15',
    2, 'SL', 'APPROVED');

... и затем запустите обновление, похожее на @Szilard Barany's (с nvl() и sum()), в результате я получаю:

select app_no, ecn, sl_entitled, sl_used, sl_unused
from leaveentitle where app_no = 1 and ecn = 1234;

APP_NO    ECN SL_ENTITLED SL_USED SL_UNUSED
------ ------ ----------- ------- ---------
     1   1234          10       2

... что выглядит хорошо; но потом, если я добавлю еще одну leaves запись:

insert into leaves (app_no, ecn, from_date, to_date, app_date,
    no_of_days, leave_type, status)
values (1, 1234, date '2012-02-01', date '2012-02-01', date '2012-01-15',
    1, 'SL', 'BOOKED');

... и снова обновить, я получаю:

select app_no, ecn, sl_entitled, sl_used, sl_unused
from leaveentitle where app_no = 1 and ecn = 1234;

APP_NO    ECN SL_ENTITLED SL_USED SL_UNUSED
------ ------ ----------- ------- ---------
     1   1234          10       5

... что явно неправильно, так как я использовал только 3 дня, а не 5. (У меня такое ощущение, что моя компания делает что-то подобное, так как мой баланс отпуска редко бывает точным). Вы можете пересчитать все в своем обновлении, а не добавлять к существующему значению; или вы можете попытаться использовать триггеры для обновления итогов; или вы можете попытаться сохранить состояние, чтобы знать, какие записи leaves уже включены. Но последние два варианта тоже становятся ужасными, не в последнюю очередь при изменении существующей записи.

Гораздо более простой вариант - удалить столбцы _used и _unused из leaveentitle и сгенерировать значения по мере необходимости, возможно, с помощью представления, облегчающего жизнь вашим пользователям, с помощью запроса типа (угадывание соединения оба условия app_no и ecn, но из вопроса не ясно):

select le.app_no, le.ecn, le.sl_entitled,
    sum(case when l.leave_type = 'SL' then l.no_of_days else 0 end) as sl_used
from leaveentitle le
join leaves l on l.app_no = le.app_no and l.ecn = le.ecn
group by le.app_no, le.ecn, le.sl_entitled;

APP_NO    ECN SL_ENTITLED SL_USED
------ ------ ----------- -------
     1   1234          10       3

... или чтобы получить все значения из исходной родительской таблицы:

create or replace view leaveview as
select le.app_no, le.ecn,
    le.sl_entitled, ls.sl_used, le.sl_entitled - ls.sl_used as sl_unused,
    le.cl_entitled, ls.cl_used, le.cl_entitled - ls.cl_used as cl_unused,
    le.lta_entitled, ls.lta_used, le.lta_entitled - ls.lta_used as lta_unused
from leaveentitle le
left join (select l.app_no, l.ecn,
        sum(case when l.leave_type = 'SL' then l.no_of_days else 0 end) as sl_used,
        sum(case when l.leave_type = 'CL' then l.no_of_days else 0 end) as cl_used,
        sum(case when l.leave_type = 'LTA' then l.no_of_days else 0 end) as lta_used
    from leaves l
    group by l.app_no, l.ecn
    ) ls on ls.app_no = le.app_no and ls.ecn = le.ecn;

select * from leaveview where app_no = 1 and ecn = 1234;

APP_NO    ECN SL_ENTITLED SL_USED SL_UNUSED CL_ENTITLED CL_USED CL_UNUSED LTA_ENTITLED LTA_USED LTA_UNUSED
------ ------ ----------- ------- --------- ----------- ------- --------- ------------ -------- ----------
     1   1234          10       3         7           5       0         5            5        0          5

Это также дает вам возможность легко добавлять дополнительные производные значения, такие как разделение на основе status, просто добавив его в представление, а не добавляя еще один сложный слой вычисления и где-то обновляя.

2 голосов
/ 20 февраля 2012

Предполагая, что APP_NO является ключом соединения между двумя таблицами, вы можете попробовать что-то вроде этого:

UPDATE leaveentitle le
SET    le.sl_used = le.sl_used + (
           SELECT CASE WHEN ls.leave_type = 'SL' THEN ls.no_of_days ELSE 0 END
           FROM   leaves ls
           WHERE  ls.app_no = le.app_no
       )
     , le.cl_used = le.cl_used + (
           SELECT CASE WHEN ls.leave_type = 'CL' THEN ls.no_of_days ELSE 0 END
           FROM   leaves ls
           WHERE  ls.app_no = le.app_no
       );

Предполагается, что leaves.app_no является первичным ключом; поэтому подзапросы гарантированно будут скалярными.

...