What is wrong with my update statement with a join in Oracle?

后端 未结 8 1353
梦谈多话
梦谈多话 2021-01-02 20:29

I am working with an Oracle 10g Database.

I have the following two tables:

T_DEBTOR :
    - ID_DEBTOR
    - HEADER
T_ELEMENT :
    -         


        
相关标签:
8条回答
  • 2021-01-02 20:35

    Good question.

    To simulate your situation, I've created sample tables:

    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.
    

    And with your current update statement, the problem becomes clear: the "not to be updated" values are set to 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.
    

    The best way to do this update, is to update a join of both tables. There are some restrictions however:

    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
    

    With the bypass ujvc hint, we can circumvent this restriction. But it is not advisable to do so unless you know really really sure that t_debtor.id_debtor is unique.

    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.
    

    It's better to just add a primary key. You'll probably have this one already in place:

    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.
    

    Regards, Rob.

    0 讨论(0)
  • 2021-01-02 20:36

    @Rob Thanks for the /*+ bypass_ujvc */ Tip. I have a couple cases where I need to use this. I wish my DBA told be able this. There are a couple times I had to create a cursor to get around this.

    0 讨论(0)
  • 2021-01-02 20:38

    Have you tried

    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;
    
    0 讨论(0)
  • 2021-01-02 20:41

    You could use the SQL Case statement, to distinguish when HEADER is null and when it has a value:
    http://www.tizag.com/sqlTutorial/sqlcase.php

    0 讨论(0)
  • 2021-01-02 20:49

    since Oracle 8i (I haven't tried with the preceeding versions), you can update a join if the tables are "key-preserved" (i-e: if you're updating the child from in a parent-child relationship). Here, if id_debtor is the primary key of T_DEBTOR, you can :

    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;
    

    Cheers,

    --
    Vincent

    0 讨论(0)
  • 2021-01-02 20:51

    You can do this by updating the results of a select, but the tables have to be 'key preserved':

    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.
    

    That creates your tables (hint - it's very useful if you can post SQL for your example!).

    Now you can update the results of a select to give what you want ...

    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
    

    This fails because the table is not key preserved, but a few constraints will solve this:

    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);
    

    Now the update will work, because the tables are key preserved:

    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
    
    0 讨论(0)
提交回复
热议问题