仅更新参数中的选定列

时间:2016-12-01 01:22:15

标签: oracle plsql

我正在尝试创建一个更新表的过程,该过程取决于所使用的参数。

下面的示例表和数据:

create table test_upd_tab
(
    co1 varchar2(100)
  , co2 varchar2(100)
  , co3 varchar2(100)
  , co4 varchar2(100)
  , co5 varchar2(100)
  , dat1 varchar2(100)
  , dat2 varchar2(100)
);

insert into test_upd_tab ( co1, co2, co3, co4, co5, dat1, dat2 ) values ( 'co1', 'co7',  'co13', 'co19', 'co25', 'dat31', 'dat37' );
insert into test_upd_tab ( co1, co2, co3, co4, co5, dat1, dat2 ) values ( 'co2', 'co8',  'co14', 'co20', 'co26', 'dat32', 'dat38' );
insert into test_upd_tab ( co1, co2, co3, co4, co5, dat1, dat2 ) values ( 'co3', 'co9',  'co15', 'co21', 'co27', 'dat33', 'dat39' );
insert into test_upd_tab ( co1, co2, co3, co4, co5, dat1, dat2 ) values ( 'co4', 'co10', 'co16', 'co22', 'co28', 'dat34', 'dat40' );
insert into test_upd_tab ( co1, co2, co3, co4, co5, dat1, dat2 ) values ( 'co5', 'co11', 'co17', 'co23', 'co29', 'dat35', 'dat41' );
insert into test_upd_tab ( co1, co2, co3, co4, co5, dat1, dat2 ) values ( 'co6', 'co12', 'co18', 'co24', 'co30', 'dat36', 'dat42' );
commit;

该表格将使用以下软件包进行更新:

create or replace package xxtest_upd_pkg
as
    procedure update_tab (p_co1  test_upd_tab.co1%type
              , p_co2  test_upd_tab.co2%type
              , p_co3  test_upd_tab.co3%type
              , p_co4  test_upd_tab.co4%type
              , p_co5  test_upd_tab.co5%type
              , p_dat1 test_upd_tab.dat1%type
              , p_dat2 test_upd_tab.dat2%type
                );

end xxtest_upd_pkg;

create or replace package body xxtest_upd_pkg
as
    procedure update_tab (p_co1  test_upd_tab.co1%type
              , p_co2  test_upd_tab.co2%type
              , p_co3  test_upd_tab.co3%type
              , p_co4  test_upd_tab.co4%type
              , p_co5  test_upd_tab.co5%type
              , p_dat1 test_upd_tab.dat1%type
              , p_dat2 test_upd_tab.dat2%type
                )
    as
    begin

        UPDATE  test_upd_tab
        SET     co1  = p_co1
              , co2  = p_co2
              , co3  = p_co3
              , co4  = p_co4
              , co5  = p_co5
        where   dat1 = p_dat1
        and     dat2 = p_dat2;

    end update_tab;

end xxtest_upd_pkg;

但是,有时候,只应更新一些列,而不是同时更新所有列。 如下所示:

begin

-- only update co1 to co3 then don't touch co4 and co5
xxtest_upd_pkg.update_tab (p_co1  => 'x'
                         , p_co2  => 'y'
                         , p_co3  => 'z'
                         , p_dat1 => 'dat31' 
                         , p_dat2 => 'dat37');

-- only update co3 to co5 hen don't touch co1 and co2
xxtest_upd_pkg.update_tab (p_co3  => 'zz'
                         , p_co4  => 'a'
                         , p_co5  => 'b'
                         , p_dat1 => 'dat33' 
                         , p_dat2 => 'dat39');

-- update co3 to null 
xxtest_upd_pkg.update_tab (p_co3  => null                           
                         , p_dat1 => 'dat35' 
                         , p_dat2 => 'dat41');                          

end;

当然这导致以下错误:

PLS-00306: wrong number or types of arguments in call to 'UPDATE_TAB'

P.S。我不能使用类似“NVL(p_co1,co1)”的内容,如下所示,因为在某些情况下我会真正传递空值。

create or replace package body xxtest_upd_pkg

    update_tab (p_co1  test_upd_tab.co1%type default null
              , p_co2  test_upd_tab.co2%type default null
              , p_co3  test_upd_tab.co3%type default null
              , p_co4  test_upd_tab.co4%type default null
              , p_co5  test_upd_tab.co5%type default null
              , p_dat1 test_upd_tab.dat1%type
              , p_dat2 test_upd_tab.dat2%type
                )
    as
    begin

        UPDATE  test_upd_tab
        SET     co1  = nvl(p_col1, co1)
              , co2  = nvl(p_col2, co2)
              , co3  = nvl(p_col3, co3)
              , co4  = nvl(p_col4, co4)
              , co5  = nvl(p_col5, co5)
        where   dat1 = p_dat1
        and     dat2 = p_dat2;

    end update_tab;

end xxtest_upd_pkg;

如何在不使用重载函数或动态SQL的情况下执行此操作?

非常感谢!

3 个答案:

答案 0 :(得分:1)

这是一种方法。

修改update_tab过程的签名,并为要更新的每个列添加一个标志。这样的事情。

procedure update_tab 
          (p_co1  test_upd_tab.co1%type,
           p_co1_flag VARCHAR2(1),   
           p_co2  test_upd_tab.co2%type,
           p_co2_flag VARCHAR2(1),
           p_co3  test_upd_tab.co3%type,
           p_co3_flag VARCHAR2(1),
           p_co4  test_upd_tab.co4%type,
           p_co4_flag VARCHAR2(1),
           p_co5  test_upd_tab.co5%type,
           p_co5_flag VARCHAR2(1),
           p_dat1 test_upd_tab.dat1%type,
           p_dat2 test_upd_tab.dat2%type
           );

像这样修改你的更新语句。

    UPDATE  test_upd_tab
    SET     co1  = CASE WHEN p_co1_flag = 'Y' then p_co1 ELSE  co1 END,
            co2  = CASE WHEN p_co2_flag = 'Y' then p_co2 ELSE  co2 END,
            co3  = CASE WHEN p_co3_flag = 'Y' then p_co3 ELSE  co3 END,
            co4  = CASE WHEN p_co4_flag = 'Y' then p_co4 ELSE  co4 END,
            co5  = CASE WHEN p_co1_flag = 'Y' then p_co1 ELSE  co5 END
    WHERE   dat1 = p_dat1
    AND     dat2 = p_dat2;

现在,对于要更新的​​任何列,您必须传递该列的“是”标志。

xxtest_upd_pkg.update_tab (p_co1  => 'a',    
                                   p_co1_flag = 'Y',        
                                   p_co2  => 'm',
                                   p_co2_flag = 'Y',
                                   p_co3  => 'z',
                                   p_co3_flag = 'Y',
                                   p_co4  => 'DUMMY',
                                   p_co4_flag = 'N',
                                   p_co5  => 'DUMMY',
                                   p_co5_flag = 'N',
                                   p_dat1 => 'dat31',
                                   p_dat2 => 'dat37');

现在,只会更新那些您将传递Y标志的列。其余列将使用其现有值进行更新。

答案 1 :(得分:0)

可能没有很多很棒的选择。

如果要使参数可选,则需要指定默认值。理论上,您可以为每列标识一个不可能但非NULL值,将其用作默认值,然后将其作为更新的一部分进行检查。但实际上,这往往是困难的,你最终会得到一堆不同的“神奇”值(-1适用于许多只支持正值的数字列,但不适用于可合法产生负数的列;日期'1800 01-01'适用于很多日期,除非您碰巧拥有一个房地产数据库,其中一些房产已有几百年的历史。)

您可以使用NULL作为默认值,然后添加一个附加参数,以标识哪些NULL值为“真实”且哪些是默认值。例如,这可能是您希望真正设置为NULL的参数集合。

create type my_collection as table of varchar2(30);

xxtest_upd_pkg.update_tab (p_co3  => null
                         , p_co4  => 'a'
                         , p_co5  => null
                         , p_co6  => null 
                         , p_attr_to_null => my_collection( 'p_co6', 'p_col5' )
                         , p_dat1 => 'dat35' 
                         , p_dat2 => 'dat41');        

当然,这意味着你要定义一个集合类型,这有点不优雅。现在,您的程序必须检查参数是否为NULL以及参数是否列在p_attr_to_null集合中。

答案 2 :(得分:0)

我要解决此问题的方法是使用DEFAULT值和CASE Staements:

create or replace package xxtest_upd_pkg
as
    procedure update_tab (p_co1  test_upd_tab.co1%type                  -- required
                        , p_co2  test_upd_tab.co2%type  default 'N/A'
                        , p_co3  test_upd_tab.co3%type  default 'N/A'
                        , p_co4  test_upd_tab.co4%type  default 'N/A'
                        , p_co5  test_upd_tab.co5%type  default 'N/A'
                        , p_dat1 test_upd_tab.dat1%type                 -- required
                        , p_dat2 test_upd_tab.dat2%type                 -- required
                          );

end xxtest_upd_pkg;

create or replace package body xxtest_upd_pkg
as
    procedure update_tab (p_co1  test_upd_tab.co1%type  default 'N/A'
                        , p_co2  test_upd_tab.co2%type  default 'N/A'
                        , p_co3  test_upd_tab.co3%type  default 'N/A'
                        , p_co4  test_upd_tab.co4%type  default 'N/A'
                        , p_co5  test_upd_tab.co5%type  default 'N/A'
                        , p_dat1 test_upd_tab.dat1%type                 -- required
                        , p_dat2 test_upd_tab.dat2%type                 -- required
                          );
    as
    begin

        UPDATE  test_upd_tab
        SET     co1  = case when p_co1 = 'N/A' then co2 else p_co1 end
              , co2  = case when p_co2 = 'N/A' then co2 else p_co2 end
              , co3  = case when p_co3 = 'N/A' then co3 else p_co3 end
              , co4  = case when p_co4 = 'N/A' then co4 else p_co4 end
              , co5  = case when p_co5 = 'N/A' then co5 else p_co5 end
        where   dat1 = p_dat1
        and     dat2 = p_dat2;

    end update_tab;

end xxtest_upd_pkg;

所以我现在可以按如下方式调用包:

begin

-- only update co1 to co3 then don't touch co4 and co5
xxtest_upd_pkg.update_tab (p_co1  => 'x'
                         , p_co2  => 'y'
                         , p_co3  => 'z'
                         , p_dat1 => 'dat31' 
                         , p_dat2 => 'dat37');

-- only update co3 to co5 hen don't touch co1 and co2
xxtest_upd_pkg.update_tab (p_co3  => 'zz'
                         , p_co4  => 'a'
                         , p_co5  => 'b'
                         , p_dat1 => 'dat33' 
                         , p_dat2 => 'dat39');

-- update co3 to null 
xxtest_upd_pkg.update_tab (p_co3  => null                           
                         , p_dat1 => 'dat35' 
                         , p_dat2 => 'dat41');                          

end;