【问题标题】:self merge in oracle 11g在 oracle 11g 中自我合并
【发布时间】:2013-11-17 01:33:47
【问题描述】:

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

我只想知道这是否可以完成。否则我将不得不坚持将查询分离回单独的更新/插入操作。

这是我目前所拥有的:

方法一:

          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 中找不到任何记录(隐式与目标不匹配),我想做的是插入,特别是因为插入语句不包含从变量而不是源本身传入的值。

我已尝试将源更新为如下所示:

                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 将始终与自身匹配。

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

【问题讨论】:

    标签: sql oracle merge


    【解决方案1】:

    合并到 MY_TARGET t 使用 (select 1 from DUAL) s 开 (t.COL1 = :p1 ) 匹配时 更新集 t.COL3 = :p3 当不匹配时 插入(COL1、COL2、COL3) 值 (:p1, :p2, :p3)

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

    【讨论】:

      【解决方案2】:

      哇,这花了我很长时间才完成!

      我在方法 3 上走在正确的轨道上(UNION ALL 与来自 d​​ual 的空记录集)。

      你只需要满足三个条件:

      1. 您始终需要从源表中返回结果集,但以与目标不匹配的方式返回。
      2. 您不能同时返回匹配集和非匹配集,否则您将同时执行插入和更新
      3. 您的主键是可更新的,因为它匹配多个列。我对它们有独特的限制,所以如果我尝试复制它会引发错误

      所以,源代码应该是这样的:

                  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
      

      【讨论】:

      • @sksallaj- 非常感谢您的解释,这正是我所期待的。但我有一个小问题。当我从 order by 子句运行此查询时,我将“COLA”作为无效标识符。这个订单是双重要求的吗?因为我们只得到一张唱片,我明白我们需要通过 ASC 订购。请在这里帮助我,似乎没有人在合并语句中解决这种情况..
      【解决方案3】:

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

      如果是这种情况,您实际上不需要在此处使用 ROWID,只需从对偶中进行选择,然后像第一次尝试一样在 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')
      

      【讨论】:

      • 是的,这没有帮助,因为更新语句只会更新 CREATEDATE 和 USERID,如果我需要更新 COLA、COLB 或 COLC 中的任何一个怎么办?像这样:UPDATE TABLEA SET COLA='1' WHERE COLA='2'。我花了一段时间,但我想我在开车回家的时候发现了一个黑客。在查看代码时,我并不明显。
      • 更新主键几乎总是一个坏主意。主键的整个想法是它应该定义记录唯一的东西并且它不应该改变。很遗憾,您留下了需要执行此操作的架构!
      • 我同意。 DBA 承包商宣布它更易于维护。我提出了将有关索引的信息与实际数据分开的观点,否则会使查询变得非常复杂。就我而言,ROWID 就足够了,因为它正在对自己进行查询。如果我要使用连接,那就会出现问题。我赞成您的答案,因为它是在建筑上合理的设计上进行自我合并的正确格式。
      猜你喜欢
      • 2014-11-21
      • 1970-01-01
      • 2015-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-09
      相关资源
      最近更新 更多