【问题标题】:What is wrong with my update statement with a join in Oracle?我在 Oracle 中加入的更新语句有什么问题?
【发布时间】:2010-11-01 19:08:34
【问题描述】:

我正在使用 Oracle 10g 数据库。

我有以下两张表:

T_DEBTOR :
    - ID_DEBTOR
    - HEADER
T_ELEMENT :
    - ID_ELEMENT
    - ID_DEBTOR
    - INSURER

这两个表使用 ID_DEBTOR 字段连接。

只有当 HEADER 不为空时,我才想用关联的 T_DEBTOR.HEADER 更新 T_ELEMENT.INSURER 值。 换句话说:

If T_DEBTOR.HEADER != null
    Then T_ELEMENT.INSURER = T_DEBTOR.HEADER
    Else T_ELEMENT.INSURER is not modified!

我尝试使用以下 SQL 查询:

update
    T_ELEMENT elt
    set elt.INSURER = (
        select HEADER
            from T_DEBTOR debtor
            where
                debtor.HEADER is not null
                and debtor.ID_DEBTOR = elt.ID_DEBTOR);

此查询适用于与 HEADER 不为空的债务人相关联的所有元素。 但是,当 T_DEBTOR.HEADER 为 null 时,此查询将 T_ELEMENT.INSURER 设置为 null,这是不正确的。

即:

If T_DEBTOR.HEADER != null
    Then T_ELEMENT.INSURER = T_DEBTOR.HEADER   --> This part is OK
    Else T_ELEMENT.INSURER is set to null      --> This part is NOT OK

我的查询有什么问题?

编辑,关于 Brian Storrar 的回答:

我想做的是这样的:

update
    T_ELEMENT elt
    set elt.INSURER = (
        select HEADER
            from T_DEBTOR debtor
            where
                debtor.HEADER is not null
                and debtor.ID_DEBTOR = elt.ID_DEBTOR)
    where debtor.HEADER is not null;

【问题讨论】:

    标签: sql oracle sql-update


    【解决方案1】:

    你试过了吗

    update
        T_ELEMENT elt
        set elt.INSURER = (
            select HEADER
                from T_DEBTOR debtor
                where
                    debtor.HEADER is not null
                    and debtor.ID_DEBITEUR = elt.ID_DEBITEUR)
    where not elt.ID_DEBITEUR is null;
    

    【讨论】:

    • 这不是我想要的。在我的程序中,elt.ID_DEBTOR 永远不会为空。我想要什么:如果关联的债务人(即 elt.ID_DEBTOR = debtor.ID_DEBTOR)的 HEADER = null,我不想更新 elt。
    【解决方案2】:

    我找到了解决问题的方法(添加了 where 子句):

    update
        T_ELEMENT elt
        set elt.INSURER = (
            select HEADER
                from T_DEBTOR debtor
                where
                    debtor.HEADER is not null
                    and debtor.ID_DEBTOR = elt.ID_DEBTOR)
        where exists (
            select null
                from T_DEBTOR debtor
                where debtor.HEADER is not null
                    and debtor.ID_DEBTOR = elt.ID_DEBTOR);
    

    如果您有更好的解决方案,请不要犹豫!

    【讨论】:

      【解决方案3】:

      你试过了吗

      update
          T_ELEMENT elt
          set elt.INSURER = NVL((
              select HEADER
                  from T_DEBTOR debtor
                  where
                      debtor.HEADER is not null
                      and debtor.ID_DEBTOR = elt.ID_DEBTOR), elt.INSURER);
      

      或类似的东西 承认这有点没有选择性,但我认为它会做你想要的。

      【讨论】:

      • 我已经尝试过您的解决方案,它奏效了!它并不比我的建议快,但是阅读起来确实要好得多!
      • 因为这是更新表中的每一行,但是,它可能会比其他选项产生更多的 REDO。如果您在此表上有审核、历史跟踪或触发器,您可能会为自己制造问题。
      【解决方案4】:

      从 Oracle 8i 开始(我没有尝试过使用之前的版本),如果表是“保留键的”,您可以更新连接(即:如果您从父子关系中更新子节点) .这里,如果 id_debtor 是 T_DEBTOR 的主键,则可以:

      UPDATE (SELECT e.insurer, d.header
                FROM t_element e, t_debtor d
               WHERE e.id_debtor = d.id_debtor
                 AND d.header IS NOT NULL)
         SET insurer = HEADER;
      

      干杯,

      --
      文森特

      【讨论】:

      • 您的解决方案也有效。我在搜索过程中看到过这种语法,但我很难准确地得到它......谢谢。
      【解决方案5】:

      您可以使用 SQL Case 语句来区分 HEADER 何时为 null 以及何时有值:
      http://www.tizag.com/sqlTutorial/sqlcase.php

      【讨论】:

        【解决方案6】:

        好问题。

        为了模拟您的情况,我创建了示例表:

        SQL> create table t_debtor(id_debtor,header)
          2  as
          3  select 1, 'Header 1' from dual union all
          4  select 2, null from dual union all
          5  select 3, 'Header 3' from dual
          6  /
        
        Tabel is aangemaakt.
        
        SQL> create table t_element (id_element,id_debtor,insurer)
          2  as
          3  select 1, 1, 'to be updated' from dual union all
          4  select 2, 1, 'to be updated' from dual union all
          5  select 3, 2, 'not to be updated' from dual union all
          6  select 4, 2, 'not to be updated' from dual union all
          7  select 5, 3, 'to be updated' from dual
          8  /
        
        Tabel is aangemaakt.
        

        使用您当前的更新语句,问题变得清晰:“不更新”值设置为 NULL:

        SQL> update
          2      T_ELEMENT elt
          3      set elt.INSURER = (
          4          select HEADER
          5              from T_DEBTOR debtor
          6              where
          7                  debtor.HEADER is not null
          8                  and debtor.ID_DEBTOR = elt.ID_DEBTOR)
          9  /
        
        5 rijen zijn bijgewerkt.
        
        SQL> select * from t_element
          2  /
        
        ID_ELEMENT  ID_DEBTOR INSURER
        ---------- ---------- -----------------
                 1          1 Header 1
                 2          1 Header 1
                 3          2
                 4          2
                 5          3 Header 3
        
        5 rijen zijn geselecteerd.
        

        进行此更新的最佳方法是更新两个表的连接。但是有一些限制:

        SQL> rollback
          2  /
        
        Rollback is voltooid.
        
        SQL> update ( select elt.insurer
          2                , dtr.header
          3             from t_element elt
          4                , t_debtor dtr
          5            where elt.id_debtor = dtr.id_debtor
          6              and dtr.header is not null
          7         )
          8     set insurer = header
          9  /
           set insurer = header
               *
        FOUT in regel 8:
        .ORA-01779: cannot modify a column which maps to a non key-preserved table
        

        通过绕过 ujvc 提示,我们可以绕过这个限制。 但除非您确实确定 t_debtor.id_debtor 是独一无二的,否则不建议这样做。

        SQL> update /*+ bypass_ujvc */
          2         ( select elt.insurer
          3                , dtr.header
          4             from t_element elt
          5                , t_debtor dtr
          6            where elt.id_debtor = dtr.id_debtor
          7              and dtr.header is not null
          8         )
          9     set insurer = header
         10  /
        
        3 rijen zijn bijgewerkt.
        
        SQL> select * from t_element
          2  /
        
        ID_ELEMENT  ID_DEBTOR INSURER
        ---------- ---------- -----------------
                 1          1 Header 1
                 2          1 Header 1
                 3          2 not to be updated
                 4          2 not to be updated
                 5          3 Header 3
        
        5 rijen zijn geselecteerd.
        

        最好只添加一个主键。你可能已经有了这个:

        SQL> rollback
          2  /
        
        Rollback is voltooid.
        
        SQL> alter table t_debtor add primary key (id_debtor)
          2  /
        
        Tabel is gewijzigd.
        
        SQL> update ( select elt.insurer
          2                , dtr.header
          3             from t_element elt
          4                , t_debtor dtr
          5            where elt.id_debtor = dtr.id_debtor
          6              and dtr.header is not null
          7         )
          8     set insurer = header
          9  /
        
        3 rijen zijn bijgewerkt.
        
        SQL> select * from t_element
          2  /
        
        ID_ELEMENT  ID_DEBTOR INSURER
        ---------- ---------- -----------------
                 1          1 Header 1
                 2          1 Header 1
                 3          2 not to be updated
                 4          2 not to be updated
                 5          3 Header 3
        
        5 rijen zijn geselecteerd.
        

        问候, 抢。

        【讨论】:

        • 最后,您使用的解决方案与文森特提供的解决方案相同,但您的解释真的很完整!谢谢...顺便说一句,T_DEBTOR.ID_DEBTOR 确实是主键。
        【解决方案7】:

        您可以通过更新选择的结果来做到这一点,但表必须是“保留键”:

        SQL> create table t_debtor ( id_debtor integer, header varchar2(10));
        
        Table created.
        
        SQL> create table t_element (id_element integer, id_debtor integer, insurer varchar2(10));
        
        Table created.
        
        SQL> insert into t_debtor values (1, 'something');
        
        1 row created.
        
        SQL> insert into t_debtor values (2, 'else');
        
        1 row created.
        
        SQL> insert into t_debtor values (3, null);
        
        1 row created.
        
        SQL>
        SQL> insert into t_element values (1, 1, 'foo');
        
        1 row created.
        
        SQL> insert into t_element values (2, 2, null);
        
        1 row created.
        
        SQL> insert into t_element values (3, 3, 'bar');
        
        1 row created.
        
        SQL> commit;
        
        Commit complete.
        

        这将创建您的表(提示 - 如果您可以为您的示例发布 SQL,这将非常有用!)。

        现在您可以更新选择的结果以提供您想要的...

        SQL> update (select e.id_element, d.header header, e.insurer insurer
                from t_debtor d, t_element e
          2          where d.id_debtor = e.id_debtor  3
          4          and d.header is not null)
          5  set insurer = header;
        set insurer = header
            *
        ERROR at line 5:
        ORA-01779: cannot modify a column which maps to a non key-preserved table
        

        这会失败,因为表没有保留键,但一些约束可以解决这个问题:

        alter table t_element add constraint t_element_pk primary key (id_element) using index;
        
        alter table t_debtor add constraint t_debtor_pk primary key (id_debtor) using index;
        
        alter table t_element add constraint t_element_debtor_fk foreign key (id_debtor) references t_debtor(id_debtor);
        

        现在更新将起作用,因为这些表已保留密钥:

        SQL> update (select e.id_element, d.header header, e.insurer insurer
                from t_debtor d, t_element e
                where d.id_debtor = e.id_debtor
                and d.header is not null)
        set insurer = header  2    3    4    5  ;
        
        2 rows updated.
        
        SQL> select * from t_element;
        
        ID_ELEMENT  ID_DEBTOR INSURER
        ---------- ---------- ----------
                 1          1 something
                 2          2 else
                 3          3 bar
        

        【讨论】:

          【解决方案8】:

          @Rob 感谢 /*+ bypass_ujvc */ 提示。我有几个需要使用它的情况。我希望我的 DBA 能够做到这一点。有几次我不得不创建一个光标来解决这个问题。

          【讨论】:

          • 他可能没有,因为这是一个未记录的提示,因此不受支持。有一些 10g 版本无法正常工作。我不会在生产代码中触及它。
          猜你喜欢
          • 1970-01-01
          • 2013-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-11
          相关资源
          最近更新 更多