根据另一个表中的值条件更新表中的值

时间:2012-02-20 07:10:33

标签: oracle stored-procedures oracle11g

我有两张桌子,如下:

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 个答案:

答案 0 :(得分:2)

假设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是主键;所以子查询保证是标量。

答案 1 :(得分:2)

不是问题的答案,但对于评论来说太大了......

尝试在父表中维护汇总数据通常不是一个好主意,因为它容易出错和混淆。例如,在这种情况下,您希望将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的更新(使用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中删除_unusedleaveentitle列并根据需要生成值,可能是通过视图使用户的生活更轻松,查询(猜测连接条件是app_noecn,但问题不明确):

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的分割,只需将其添加到视图中,而不必添加另一个复杂的计算层并在某处更新。