zoukankan      html  css  js  c++  java
  • [转载]Oracle Merge的使用

    FROM: http://zhangqchang.blog.163.com/blog/static/464989732009219114653226/

    摘至网上的几个例子

    一、
    *****************************************************************
    Merger into是oracle从9i开始增加的一个语句,从merge的字面上的意思:合并,

    兼并不难理解merge在oracle中的含义,merge在oracle所起的作用是:如果你从

    以组值中有选择的更新和插入到到一张表,具体来说是:如果该表中已经匹配了

    这组值的某些条件,那么可以使用这组值的部分数据来更新这个表的,如果该表

    中无法匹配了这组值的某些条件,那么可以使用这组值的数据来为这个表新增一

    条数据。
    无论你在使用任何DBMS,你总是难以避免的将会遇到上面提到的这种需求,如果

    你不使用merge语句,你将会不得不在程序中增加大段的代码,或者是在oracle用

    很长的代码来实现。好在现在我们有了merge,可以帮我们省下很多时间。
    好了废话少说:
    Merge 的基本语法是这样的
    Merge into table[alias]
    Using table or sql query [alias]
    On condition
    When matched then
    Update set ….
    When not matched then
    Insert values…
    以上是merge的基本语法,其中alias是为表或者查询写的别名
    如果你看着空洞的语法觉得头很痛,看下面的例子吧
    首先我们创建两个表
    create table test1(id int,name varchar(20));
    create table test2(id int,name varchar(20))
    然后随意插入几行数据
    insert into test1 values(1,’hi’);
    insert into test1 values(2,’hello’);
    insert into test2 values(2,’你好’);
    insert into test2 values(3,’morning’);
    下面我们要使用了merge了,将test2中的数据有选择地转移或者更新到test1中
    如果你运行了下下面的merge语句,你将会的到一个错误,这是因为oracle规定在

    merge语句中不能更新作为连接的列,也就是on后面的那些列
    merge into test1 t1
    using test2 t2
    on (t1.id = t2.id)
    when matched then
    update set t1.id = t2.id
    when not matched then
    insert values(t2.id,t2.name);
    所以将会得到如下的错误,虽然这个错误翻译的并不怎么样,甚至带有明显的误


    on (t1.id = t2.id)
    *
    ERROR 位于第 3 行:
    ORA-00904: “T1″.”ID”: 无效的标识符
    好了,知错就改,我们再来运行下面的merge语句。
    merge into test1 t1
    using test2 t2
    on (t1.id = t2.id)
    when matched then
    update set t1.name = t2.name
    when not matched then
    insert values(t2.id,t2.name)
    成功执行了,我们来验证一下。
    select * from test1;

    ID NAME
    —— —————-
    1 hi
    2 你好
    3 morning
    至此,你已经掌握了merge语句中的大部分。但我们还要提醒一些特殊情况。
    如果我们再向test2中增加一条语句
    insert into test2 values(2,’早’)
    再执行我们以已经成功执行过的merge语句,将会遇到下面的错误
    SQL> merge into test1 t1
    2 using test2 t2
    3 on (t1.id = t2.id)
    4 when matched then
    5 update set t1.name = t2.name
    6 when not matched then
    7 insert values(t2.id,t2.name)
    8 ;
    using test2 t2
    *
    ERROR 位于第 2 行:
    ORA-30926: 无法在源表中获得一组稳定的行
    这是因为当执行到t1.id = t2.id =2时,test2表中对应了两条记录,无法进行更

    新或者插入。所以就出错了。所以你应该明白oracle中的merge语句应该保证on中

    的条件的唯一性,
    另外一点需要说明的是using关键字后面可以接表,当然也可以接其他的select语

    句做出来的一个类视图,oracle中的这种结构,我们在前面已经介绍多次,在此

    不作介绍。


    二、
    *****************************************************************
    Merge Into 语句代替Insert/Update在Oracle中的应用实战收藏
    动机:

    想在Oracle中用一条SQL语句直接进行Insert/Update的操作。

    说明:

    在进行SQL语句编写时,我们经常会遇到大量的同时进行Insert/Update的语句 ,

    也就是说当存在记录时,就更新(Update),不存在数据时,就插入(Insert)。

    实战:

    接下来我们有一个任务,有一个表T,有两个字段a,b,我们想在表T中做

    Insert/Update,如果存在,则更新T中b的值,如果不存在,则插入一条记录。在

    Microsoft的SQL语法中,很简单的一句判断就可以了,SQL Server中的语法如下

    if exists(select 1 from T where T.a='1001' ) update T set T.b=2 Where

    T.a='1001' else insert into T(a,b) values('1001',2);

    以上语句表明当T表中如果存在a='1001' 的记录的话,就把b的值设为2,否则就

    Insert一条a='100',b=2的记录到T中。

    但是接下来在Oracle中就遇到麻烦了,记得在Oracle 9i之后就有一条Merge into

    的语句可以同时进行Insert 和Update的吗,Merge的语法如下:

    MERGE INTO table_name alias1 
    USING (table|view|sub_query) alias2
    ON (join condition) 
    WHEN MATCHED THEN 
        UPDATE table_name 
        SET col1 = col_val1, 
            col2     = col2_val 
    WHEN NOT MATCHED THEN 
        INSERT (column_list) VALUES (column_values); 
     

    上面的语法大家应该都容易懂吧,那我们按照以上的逻辑再写一次。

    MERGE INTO T T1
    USING (SELECT a,b FROM T WHERE t.a='1001') T2
    ON ( T1.a=T2.a)
    WHEN MATCHED THEN
      UPDATE SET T1.b = 2
    WHEN NOT MATCHED THEN 
      INSERT (a,b) VALUES('1001',2);
    以上的语句貌似很对是吧,实际上,该语句只能进行更新,而无法进行Insert,

    错误在哪里呢?

    其实在Oracle中Merge语句原先是用来进行整表的更新用的,也就是ETL工具比较

    常用的语法,重点是在Using上。

    用中文来解释Merge语法,就是:

    在alias2中Select出来的数据,每一条都跟alias1进行 ON (join condition)的

    比较,如果匹配,就进行更新的操作(Update),如果不匹配,就进行插入操作

    (Insert)。

    因此,严格意义上讲,”在一个同时存在Insert和Update语法的Merge语句中,总

    共Insert/Update的记录数,就是Using语句中alias2的记录数。”

    以上这句话也就很好的解释了在上面写的语句为何只能进行Update,而不能进行

    Insert了,因为都Select不到数据,如何能进行Insert呢:)

    接下来要改成正确的语句就容易多了,如下:

    MERGE INTO T T1
    USING (SELECT '1001' AS a,2 AS b FROM dual) T2
    ON ( T1.a=T2.a)
    WHEN MATCHED THEN
      UPDATE SET T1.b = T2.b
    WHEN NOT MATCHED THEN 
      INSERT (a,b) VALUES(T2.a,T2.b);
    查询结果,OK!

    注意:

    如果不懂Merge语句的原理,Merge语句是一条比较危险的语句,特别是在您只想

    更新一条记录的时候,因为不经意间,你可能就把整表的数据都Update了一

    遍.....汗!!!

    我曾经犯过的一个错误如下所示,大家看出来是什么问题了吗?

    MERGE INTO T T1
    USING (SELECT Count(*) cnt FROM T WHERE T.a='1001') T2
    ON (T2.cnt>0)
    WHEN MATCHED THEN
      UPDATE SET T1.b = T2.b
    WHEN NOT MATCHED THEN 
      INSERT (a,b) VALUES(T2.a,T2.b);


    三、
    *********************************************************
    所有的MIS系统都存在一个同样的需求,就是对于特定的数据,在一次批量操作过

    程中,如果数据已经存在,则对存在的数据按照现有情况进行更新,如果不存在

    ,则需要加入数据库。这时,我们就可以考虑采用 Oracle 的 MERGE 函数,其具

    体用法如下:

    MERGE INTO [your table-name] [rename your table here]

        USING

            (

                [write your query here]

            )[rename your query-sql and using just like a table]

        ON

            ([conditional expression here] AND [...]...)

        WHEN

            MATHED

        THEN

            [here you can execute some update sql or something else ]

        WHEN

            NOT MATHED

        THEN

            [execute something else here ! ]

    下面我再进行详细的说明:

       上述代码格式中的加粗字体表示为 Oracle 关键字,[]以及其中的文字均是说

    明,在实际使用中不应有 [ words ] 出现。要注意()[圆括号]也是程序的组成

    部分。

       为了能够使问题与实际问题更加贴切,不妨假设我们现在要给计算机系某个班

    的学生批量录入学生成绩。但是,录入时,如果学生的成绩已经存在时,老师只

    想对成绩进行修改,而如果成绩不存在则直接添加到库中。我们就老师的这些需

    求来构造一个执行语句。

    DEFINE TABLE :

        SCORE :  using for save the students' score informations

        STUDENTS : the base information of students

    DEFINE COLUMNS :

        STUNO : the students' ID in the University

        STUNAME : students' name

        COURSENAME : course name

        COURSESCORE : the study-results of the reference course

        CLASSNAME : where the students study in

        STUGRADE : the students grade

        TERMNAME : the term which the reference course studied

    NOW BEAGIN TO WRITE DOWN THE STATEMENT HERE BLOW THIS LINE !

    MERGE INTO SCORE S

        USING

            (

                SELECT A.*,B.*,? MYSCORE FROM SCORE A,STUDENT B

                WHERE

                    A.CLASSNO=? AND A.GRADE=?

                    AND A.TERMNAME=? AND A.COURSENAME=?

                    A.STUNO=B.STUNO(+)

            )X

        ON

            (S.STUNO=X.STUNO)

        WHEN

            MATHED

        THEN

            UPDATE SET COURSESCORE=X.MYSCORE

        WHEN

            NOT MATHED

        THEN

            INSERT

                (

                    STUNO,STUNAME,COURSENAME,COURSESCORE,

                    CLASSNAME,STUGRADE,TERMNAME

                )

            VALUES

                (

                    X.STUNO,X.STUNAME,X.COURSENAME,X.MYSCORE,

                    X.CLASSNAME,X.STUGRADE,X.TERMNAME

                );

    注意到 MERGE 语句在最后的“;”(分号),这仅仅带到 MERGE 为一条完整的

    SQL 语句

       这时,如果你需要在你的 Java 程序中使用上述方法执行相应操作,则仅需要

    将其放入一个 for 循环中即可。由于是批量更新数据,因此,如果你不想对中间

    出现异常的数据进行提交,导致数据的不完整,则可以考虑使用 Java 的事务回

    滚机制。具体示例代码如下:

    public yourMethod(statement,...){

        try{

            Connection conn=...;

            PreparedStatement ps=...;

            Resultset rs=...;

            conn.setAutoCommit(false);

            for(int i=0;i<...;i++){

                //add your code here !

                ......

                ps.addBatch();

            }

            ps.executeBatch();

            conn.commit();

        }catch(Exception e){

            try{

                conn.rollback();

            }catch(Exception el){}

        }

    }

       这时,你会发现,在代码中直接使用 Merge 时,代码会变的非常复杂,首先

    是 SQL 的拼接变得非常复杂,接下来便是程序写完后的查错。因此,自然而然就

    会想到使用存储过程。接下来,我们来看看如何使用存储过程实现 Merge 调用过

    程。

    Oracle 存储过程定义格式如下:

    CREATE OR REPLACE PROCEDURE PRO_YOUR_PROCEDURE (

        ELEMENT_01     IN      ELEMENT_TYPE,  --COMMENTS

         .......       ....        .....            ....

        ELEMENT_0S     OUT     ELEMENT_TYPE,  --COMMENTS

          ....         ...         ...           ....

    )

    AS

        ARGUMENT_01    ARGUMENT_TYPE(ARGUMENT_RANGE);

        ...................

    BEGIN

        MERGE INTO YOUR_TABLE_NAEM [RENAEM_YOUR_TABLE_HERE]

        --AND YOUR CODE HERE !

    END;

    EXCEPTION

    WHEN

        OTHERS

    THEN

        RAISE_APPLICATION_ERROR(-20003,[YOUR EXCEPITON MESSAGE HERE !]);

    END;

    COMMIT;--IF YOUR WANT , JUST DO SO !

    END PRO_YOUR_PROCEDURE;

       其中,[RAISE_APPLICATION_ERROR(-20003,[YOUR EXCEPITON MESSAGE HERE

    !]);]中的“-20003”是 Oracle 提供的用于用户进行错误自定义的扩充代码。其

    值可以随便定义,但是也有范围: -20000 到-20999的负整数。

    接下来就是如何来在 Java 程序中调用你的存储过程。Oracle为了方便开发人员

    调用其存储过程,开发了一个 [ OracleCallableStatement ]  位于

    oracle.jdbc 包内。

    核心代码如下:

    OracleCallableStatement cal = null;

    cal=(OracleCallableStatement)conn.getMetaData().getConnection

    ().prepareCall("call PRO_......");

    ........

    .............

    .......

    for(………………){

           …………

        cal.setDouble(i,ARGUMENTS);

        …………

      cal.executeUpdate();

    }

  • 相关阅读:
    Thread中带参方法无法使用之解决方案
    项目相关的风险要素及分类
    AspNetPager分页示例之DataGrid(PostBack分页)
    Substitution 类 (asp.net 2.0 )
    自定义HTTP处理程序显示图片(asp.net 2.0)
    常见文件扩展名和它们的说明
    基于.NET的开源GIS项目(转)
    项目开发流程标准
    AOP(Aspect Oriented Programming) 面向方面编程
    项目实施及管理标准
  • 原文地址:https://www.cnblogs.com/wmx3ng/p/4558514.html
Copyright © 2011-2022 走看看