自我合并在oracle 11g中

时间:2013-11-05 23:56:53

标签: sql oracle merge

通常情况下,我们需要使用两个不同的查询执行更新或插入同一个表。我想看看是否可以使用merge语句在表上完成。

我想知道是否可以这样做。否则,我将不得不坚持将查询分离回更新/插入操作。

这是我到目前为止所做的:

方法1:

          MERGE INTO TABLEA TARGET
          USING (
              SELECT 1 FROM DUAL
          ) SOURCE
          ON (TARGET.TARGET.COLA = '001'
              AND TARGET.TARGET.COLB = '1111111'
              AND TARGET.COLC = '201302'
              )
          WHEN MATCHED THEN
             UPDATE SET TARGET.COLA = '001'
                        ,TARGET.COLB = '1111111'
                        ,TARGET.COLC = '201304'
                        ,TARGET.CREATEDATE = SYSDATE
                        ,TARGET.USERID = 'USERA'
          WHEN NOT MATCHED THEN
             INSERT (TARGET.COLA
                     ,TARGET.COLB
                     ,TARGET.COLC
                     ,TARGET.COLD
                     ,TARGET.CREATEDATE
                     ,TARGET.USERID)
             VALUES('001'
                    ,'1111111'
                    ,'201304'
                    ,'123'
                    ,SYSDATE
                    ,'USERA')

起初这个方法对我有意义,因为我总是从源返回结果,并且我会相应地更新和插入。但是,oracle拒绝遵循这个:

SQL错误:ORA-38104:无法更新ON子句中引用的列:“TARGET”。“EFF_FISCAL_YR_PD_NBR” 38104. 00000 - “无法更新ON子句中引用的列:%s” *原因:UPDATE SET的LHS包含ON子句中引用的列

方法2:

          MERGE INTO TABLEA TARGET
          USING (
                SELECT ROWID AS RID,COLA,COLB,COLC
                FROM TABLEA
                WHERE COLA = '001'
                      AND COLB = '1111111'
                      AND COLC = '201301'
          ) SOURCE
          ON (TARGET.ROWID = SOURCE.RID)
          WHEN MATCHED THEN
             UPDATE SET TARGET.COLA = '001'
                        ,TARGET.COLB = '1111111'
                        ,TARGET.COLC = '201304'
                        ,TARGET.CREATEDATE = SYSDATE
                        ,TARGET.USERID = 'USERA'
          WHEN NOT MATCHED THEN
             INSERT (TARGET.COLA
                     ,TARGET.COLB
                     ,TARGET.COLC
                     ,TARGET.COLD
                     ,TARGET.CREATEDATE
                     ,TARGET.USERID)
             VALUES('001'
                    ,'1111111'
                    ,'201304'
                    ,'123'
                    ,SYSDATE
                    ,'USERA')

这背后的逻辑是,如果我尝试从源表中查找值并且匹配,它将找到记录并使用这些值更新自身。但是,如果不匹配则尝试插入时会出现问题。因为源被过滤了,所以没有返回任何记录,因此目标没有任何内容可以匹配,也没有任何内容被插入。我想要做的是插入如果在SOURCE中找不到记录(隐式地不与目标匹配),特别是因为insert语句不包含从变量而不是源本身传入的值。

我尝试将源代码更新为:

                SELECT ROWID AS RID,COLA,COLB,COLC
                FROM TABLEA
                WHERE COLA = '001'
                      AND COLB = '1111111'
                      AND COLC = '201301'
                UNION ALL
                SELECT ROWID,NULL,NULL,NULL FROM DUAL

但问题是合并会对匹配的记录进行更新,并且记录中的插入不匹配。

对于那些想知道我为什么使用ROWID的人。这是因为设计(不是由我)表明COLA和COLB将组合在一起作为表格索引的主键。不允许复制COLA,COLB和COLC,但它们都可通过前端界面更新。我理解ROWID的缺陷,但因为我只使用一个表作为目标和源,无论我在表上执行任何CRUD操作,ROWID将始终匹配到自身。

总结:我只在对匹配项执行更新时才进行自我合并,但插入不起作用。

3 个答案:

答案 0 :(得分:1)

如果我理解正确,COLA,COLB和COLC是TABLEA的复合主键

如果是这种情况,你实际上并不需要在这里使用ROWID,只需从dual中选择,然后在你的ON语句中使用复合键就像你在第一次尝试中那样,就可以做你需要的。

您不需要更新主键列,因此可以在ON子句中使用它们。

      MERGE INTO TABLEA TARGET
      USING (
            SELECT '001' COLA,
                   '1111111' COLB,
                   '201301' COLC
            FROM DUAL
      ) SOURCE
      ON (TARGET.COLA = SOURCE.COLA
          AND TARGET.COLB = SOURCE.COLB
          AND TARGET.COLC = SOURCE.COLC
          )
      WHEN MATCHED THEN
         UPDATE SET TARGET.CREATEDATE = SYSDATE
                    ,TARGET.USERID = 'USERA'
      WHEN NOT MATCHED THEN
         INSERT (TARGET.COLA
                 ,TARGET.COLB
                 ,TARGET.COLC
                 ,TARGET.COLD
                 ,TARGET.CREATEDATE
                 ,TARGET.USERID)
         VALUES('001'
                ,'1111111'
                ,'201304'
                ,'123'
                ,SYSDATE
                ,'USERA')

答案 1 :(得分:1)

哇,这花了我很长时间才做到了!

我使用方法3进行了正确的操作(UNION ALL使用双重的空记录集)。

您只需要满足三个条件:

  1. 您始终需要从源表中返回结果集,但其方式与目标不匹配。
  2. 您不能同时返回匹配集和非匹配集,否则您将同时执行插入和更新
  3. 您的主键是可更新的,因为它在多列上匹配。我对它们有唯一的约束,所以如果我试图制作重复项就会引发错误
  4. 所以,这是源应该是什么样子:

                SELECT RID,COLA,COLB,COLC FROM
                (
                    SELECT ROWID AS RID,COLA,COLB,COLC
                    FROM TABLEA
                    WHERE COLA = '001'
                        AND COLB = '1111111'
                        AND COLC = '201301'
                    UNION ALL
                    SELECT ROWID,NULL,NULL,NULL FROM DUAL
                    ORDER BY COLA ASC
                ) f
                WHERE ROWNUM <= 1
    

    所以你返回一条记录。如果满足where子句,则在ASCENDING ORDER中对数据集进行排序,并仅返回顶部记录集。这样,合并将基于此更新。如果where子句(不是包含ROWNUM的子句)返回零值,它仍将返回空记录集,并且合并将基于该插入

    多条记录

    如果你真的想获得坚果并获得多个记录(在我的情况下,我需要1),那么你必须使用聚合(或分析函数)和东西得到匹配记录集的计数它变成一个变量,以便where子句标准如下所示:

    WHERE ROWNUM <= COUNTOFRETURNEDRESULTS
    

答案 2 :(得分:0)

合并到MY_TARGET t    使用(从DUAL中选择1)s     on(t.COL1 =:p1)   当匹配时    更新集t.COL3 =:p3  什么时候不匹配    插入(COL1,COL2,COL3)     值(:p1,:p2,:p3)

你必须有一些东西要返回才能插入