tags:

views:

2867

answers:

7

I am working with an Oracle 10g Database.

I have the following two tables:

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

These two tables are joined using the ID_DEBTOR field.

I want to update the T_ELEMENT.INSURER value with the associated T_DEBTOR.HEADER only if HEADER is not null. In others words:

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

I tried to use the following SQL query:

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

This query is working for all elements linked to debtors that has a HEADER not null. However, when the T_DEBTOR.HEADER is null, then this query set the T_ELEMENT.INSURER to null, which is not correct.

ie:

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

What is wrong with my query?

Edit, regarding the Brian Storrar answer:

What I want to do is something like that:

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;
A: 

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;
Hooloovoo
That's not what I am looking for. In my program, elt.ID_DEBTOR is never null. What I want: I do not want to update the elt if the associated Debtor (i.e. elt.ID_DEBTOR = debtor.ID_DEBTOR) has a HEADER = null.
romaintaz
A: 

I've found a solution to solve my problem (the where clause is added):

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

If you have a better solution, do not hesitate to post it!

romaintaz
+1  A: 

Have you tried

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

or something similar admittedy this is a bit unselective but I think it will do what you intend.

stjohnroe
I've tried your solution, and it worked! It is not faster than my suggestion, however it is really much better to read!
romaintaz
+1  A: 
Vincent Malgrat
Your solution is working too. I've seen this syntax during my searches, but I had some difficulties to get it exactly... Thanks.
romaintaz
A: 

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

John Smithers
+2  A: 

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.

Rob van Wijk
Finally, you use the same solution as the one provided Vincent, but your explanations are really complete! Thanks...By the way, T_DEBTOR.ID_DEBTOR is indeed a primary key.
romaintaz
+1  A: 

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
Stephen ODonnell