【问题标题】:Update with complex joins使用复杂连接进行更新
【发布时间】:2017-02-02 14:33:52
【问题描述】:

我正在尝试使用一种复杂的源数据集更新表。有一个表格包裹,其中包含有关产品的库存信息。它还包含现场产品描述。该字段有时仍有一些旧的描述,需要通过基础数据表进行更新。

但连接表还需要通过由另一个子选择或连接完成的语言进行过滤。

SELECT products.DESCSH as old_desc
        ,parcel.DESCSH  as new_desc
    FROM parcel
        ,products
   WHERE parcel.PRODU_INTERNUM = products.INTERNUM
     AND parcel.NUM LIKE '2%REGEN'
     and (SELECT COMPA.LANG FROM COMPA WHERE COMPA.NUM = parcel.COMPA_NUM)  = products.LANG
     and trim(upper(products.DESCSH)) <> trim(upper(parcel.DESCSH))

这是基本选择。 (任务基本上是用old_desc更新new_desc)

现在因为我不知道如何做到这一点,所以我在这里看到了 3 种方法。 MERGE 方法

 merge into gc_prcel target
  using (
         Select p.descsh
               ,p.internum
           from p 
               ,par
          where sysdate between p.vldty_beg and p.vldty_end
            and p.internum = par.produ_internum
            and p.lang = (SELECT COMPA.LANG FROM COMPA WHERE COMPA.NUM = par.COMPA_NUM)
        ) source
    on (target.produ_internum = source.internum)
   when matched then update
    set target.descsh = source.descsh 
  where target.num like '2%REGENSDO'
    and trim(upper(target.descsh)) <> trim(upper(target.descsh));

当它只应该更新 400 条记录并且我不知道为什么时它几乎更新了整个表,或者它陷入了无限循环(甚至更无能)。我知道这看起来很奇怪,我为那些 where 子句尝试了很多不同的地方,但没有一个有效。

带有 EXIST 子句的方法(我可以使用这个,但我发现其他方法更优雅)以及直接从子选择中更新字段的方法:

update
    (SELECT products.DESCSH as old_desc
            ,parcel.DESCSH  as new_desc
        FROM parcel
            ,products
       WHERE parcel.PRODU_INTERNUM = products.INTERNUM
         AND parcel.NUM LIKE '2%REGENSDO'
         and (SELECT COMPA.LANG FROM COMPA WHERE COMPA.NUM = parcel.COMPA_NUM)  = products.LANG
         and trim(upper(products.DESCSH)) <> trim(upper(parcel.DESCSH))) descriptions
  set descriptions.new_desc = descriptions.old_desc;

这个抛出(ORA-01779:无法修改映射到非键保留表的列)

我正在尝试使用合并语句完成的任务,还是我必须使用存在的更新以及为什么我的第一种方法失败并出现 ORA-01779)

【问题讨论】:

    标签: sql oracle merge


    【解决方案1】:

    你正在做一件聪明的事——在 MERGE 和 UPDATE 中——只有当值发生变化时才更新一行。但是在 MERGE 中你写了这个——你能发现错误吗? (这可能是执行时间长的原因或原因之一。)

    and trim(upper(target.descsh)) <> trim(upper(target.descsh));
    

    好的,这不应该是猜谜游戏;其中一个参数应该是source.deschs,两边都有target

    编辑:或者实际上等待,这应该会导致语句不更新任何内容,因为该条件永远不会为 TRUE。这正是您使用的陈述吗? 结束编辑

    对于 UPDATE 和您遇到的错误,我在 Stack Overflow 的“文档”域中写了这篇短文。它解释了您遇到的错误以及如何解决它:Update with joins

    【讨论】:

    • 嗨。非常感谢您的快速回答...实际上这是我最后使用的声明。但我不得不取消它,因为它不会停止执行
    • @JanZimmermann - 这真的很奇怪。您在某些运行中说过该语句更新了许多行,但 where 子句永远不会是 TRUE (事物与自身不同永远不会是真的)。那怎么可能呢?
    • 是的,我知道你的意思 :) 但我也说过我尝试了这个语句的许多不同变体,如果它没有抛出错误或无限期运行,它将更新 80k 记录而不是400. 不幸的是,中间的那些步骤对我来说不再可用了。顺便说一句,我已经必须完成任务,所以我使用了存在的方法.. 但我仍然很好奇我如何通过 Merge 语句完成该任务
    【解决方案2】:

    如果 gc_prcel 和 par(我假设这确实是您在更新语句中提到的 parcel 表?)是不同的表,那么我会进行合并,例如:

    merge into gc_prcel target
      using (
             Select p.descsh
                   ,p.internum
               from p
                    inner join par on p.internum = par.produ_internum
                                      and trim(upper(p.descsh)) <> trim(upper(par.descsh)
              where sysdate between p.vldty_beg and p.vldty_end
                and p.lang = (SELECT COMPA.LANG FROM COMPA WHERE COMPA.NUM = par.COMPA_NUM)
                and par.num like '2%REGENSDO');
            ) source
        on (target.produ_internum = source.internum)
       when matched then update
        set target.descsh = source.descsh;
    

    但是,如果您要更新的是宗地表,我会使用类似的内容:

    merge into parcel tgt
      using (select p.internum,
                    p.descsh,
                    compa.num
             from   products p
                    inner join compa on p.lang = compa.lang
             where  sysdate between p.vldty_beg and p.vldty_end) src
        on (tgt.produ_internum = src.internum
            and tgt.num like '2%REGENSDO'
            and tgt.compa_num = src.compa_num)
    when matched then
      update set tgt.descsh = src.descsh
      where      trim(upper(tgt.descsh)) != trim(upper(src.descsh));
    

    【讨论】:

    • 感谢您的快速回答(我不知道你们这么快:))我现在没有时间测试它,但明天早上会做第一件事。是的 gc_prcel 和 parcel 是相同的(我以为我小心不要发布实际的对象名称:/)
    • 在子子选择中,您引用了 par.compa_num 但在任何地方都没有设置 par 别名。我想我也需要在子选择中加入包裹表,因为 compa_num 仅在那里可用,我需要该字段来确定语言(LANG)
    • @JanZimmermann 好的,我已经更新了我的答案;不知何故,我错过了它指的是par.compa_num中的par而不是p的事实。呵呵!
    猜你喜欢
    • 2015-03-20
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多