(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Изменение соединения

Источник: ln

Эта статья посвящена "хитрым" операторам UPDATE, изменяющим данные в одной таблице на основе данных из другой. Попутно обсуждаются причины возникновения ошибки ORA-01779 при выполнении таких действий путем изменения соединения. По мотивам очередного ответа Тома Кайта.

Оператор UPDATE и NULL-значения

Том,

При изменении столбца с помощью оператора update, значение некоторых записей (которые не надо менять) изменяется на NULL. Я использую следующий оператор:

update    table    name B
  set        columnname    = 
    (    select    value
          from     lookup    O
          where    B.keyname = O.keyname
            and    O.Othercolumn = Other_value);

В результате выполняются все необходимые изменения, но изменяются и записи, которые не надо менять: они получают значения Null. Можно ли этого избежать, поскольку нам надо часто изменять записи, но не все сразу.

Нет ли способа изменить только те записи, которые нужно, и не сбросить значения в других записях в Null?

Ответ Тома Кайта

Есть как минимум 2 способа правильно выполнить такого рода коррелированное изменение. Я покажу свой любимый метод (изменение соединения) и другой метод, который будет работать если нельзя задать ограничение уникальности по LOOKUP(keyname) (что необходимо для успешного изменения соединения).

Рассмотрим следующие тестовые таблицы:

scott@ORA734.WORLD> create table name
  2  ( keyname int,
  3    columnName varchar2(25)
  4  )
  5  /
Table created.

scott@ORA734.WORLD> create table lookup
  2  ( keyname int PRIMARY KEY,
  3    value varchar2(25),
  4    otherColumn int
  5  )
  6  /
Table created.

scott@ORA734.WORLD> insert into name values ( 100, 'Original Data' );
1 row created.

scott@ORA734.WORLD> insert into name values ( 200, 'Original Data' );
1 row created.

scott@ORA734.WORLD> insert into lookup values ( 100, 'New Data', 1 );
1 row created.

scott@ORA734.WORLD> commit;
Commit complete.

А вот параметр "other_value", который вы используете в своем операторе update...

scott@ORA734.WORLD> variable other_value number
scott@ORA734.WORLD> exec :other_value := 1
PL/SQL procedure successfully completed.

scott@ORA734.WORLD> select * from name;

   KEYNAME COLUMNNAME
---------- -------------------------
       100 Original Data
       200 Original Data

Далее мы изменяем соединение. Можно изменять значение столбцов только в одной из таблиц, а другие таблицы, которые мы не изменяем, должны быть "защищены ключом" - мы должны иметь гарантию, что при соединении NAME со второй таблицей будет возвращено не более одной записи. Для этого столбец keyname в таблице LOOKUP должен быть либо первичным ключом, либо быть связанным ограничением уникальности...

scott@ORA734.WORLD> update
  2    ( select columnName, value
  3        from name, lookup
  4       where name.keyname = lookup.keyname
  5         and lookup.otherColumn = :other_value )
  6     set columnName = value
  7  /

1 row updated.

scott@ORA734.WORLD> select * from name;

   KEYNAME COLUMNNAME
---------- -------------------------
       100 New Data
       200 Original Data

Обратите внимание, изменилась только интересующая нас строка - остальные данные не затронуты...

scott@ORA734.WORLD> rollback;
Rollback complete.

scott@ORA734.WORLD> select * from name;

   KEYNAME COLUMNNAME
---------- -------------------------
       100 Original Data
       200 Original Data

Следующий способ сработает безо всяких ограничений - не нужно первичного ключа млм ограничения уникальности для таблицы lookup (но лучше точно знать, что подзапрос вернет 0 или 1 запись!).

Оператор очень похож на ваш update, но имеет конструкцию where, чтобы изменялись только строки, для которых есть соответствия...

scott@ORA734.WORLD> update name
  2     set columnName = ( select value
  3                          from lookup
  4                         where lookup.keyname = name.keyname
  5                           and otherColumn = :other_value )
  6   where exists ( select value
  7                    from lookup
  8                   where lookup.keyname = name.keyname
  9                     and otherColumn = :other_value )
 10  /

1 row updated.

scott@ORA734.WORLD> select * from name;

   KEYNAME COLUMNNAME
---------- -------------------------
       100 New Data
       200 Original Data

А можно ли добиться того же для нескольких столбцов?

Том,

Мне не удалось сделать следующее. Я создал две таблицы t и t1 с помощью select * from user_objects. В таблицах t и t1 - по 117 записей. Затем я взял один object_name(PRIMARY_SALESFORE) в таблице t и изменил три записи - object_type, object_id и created.

ADHOC@VOYAGER> select t.object_id,t1.object_id,t.object_type,t1.object_type,
  2  t.created,t1.created
  3  from t,t1
  4  where t1.object_name=t.object_name
  5  and t.object_name='PRIMARY_SALESFORCE';

OBJECT_ID OBJECT_ID OBJECT_TYPE   OBJECT_TYPE   CREATED   CREATED
--------- --------- ------------- ------------- --------- ---------
     2222     17927 ORATAB        TABLE         02-APR-02 01-APR-02

Теперь при попытке изменить эти три поля в таблице t на основе t1 я получаю следующее сообщение об ошибке. Что я делаю не так?

1  update
2  (select t.object_id,t1.object_id,t.object_type,t1.object_type,
3  t.created,t1.created
4  from t,t1
5  where t1.object_name=t.object_name
6  and t.object_name='PRIMARY_SALESFORCE')
7  set t.object_id=t1.object_id,
8  t.object_type=t1.object_type,
9* t.created=t1.created
ADHOC@VOYAGER> /
update
*
ERROR at line 1:
ORA-00904: invalid column name

При попытке изменить всего один столбец в T я тоже получаю сообщение об ошибке:

ADHOC@VOYAGER> update 
  2  (select t.object_id toid,t1.object_id t1oid
  3  from t,t1
  4  where t.object_name=t1.object_name
  5  and upper(t.object_name)='PRIMARY_SALESFORCE')
  6  set toid=t1oid;             
update
*
ERROR at line 1:
ORA-01779: cannot modify a column which maps to a non key-preserved table 

Ответ Тома Кайта

Таблицы T и T1 "невидимы" за пределами подставляемого представления (inline view). Вы это исправили, добавив псевдонимы toid, t1oid.

Что касается второй ошибки, пожалуйста, перечитайте представленный выше ответ. НАДО, чтобы столбец object_id таблицы T1 был связан ограничением первичного ключа/уникальности, чтобы каждая строка таблицы T соединялась НЕ БОЛЕЕ, чем с одной строкой таблицы T1.

Пусть в таблице T имеется:

OBJECT_NAME          OBJECT_ID
--------------       ----------------
ABC                  xxxx

А в таблице T1:

OBJECT_NAME          OBJECT_ID
----------------     ------------------
ABC                  123
ABC                  456

что должно быть в результате:

ADHOC@VOYAGER> update 
  2  (select t.object_id toid,t1.object_id t1oid
  3  from t,t1
  4  where t.object_name=t1.object_name
  5  and upper(t.object_name)='PRIMARY_SALESFORCE')
  6  set toid=t1oid;  

надо ли при соединении брать 123 или 456, и в каких случаях? Поскольку результат этого изменения на 100% НЕОДНОЗНАЧНЫ, мы его не допускаем. Необходимо гарантировать защиту ключом с помощью ограничения первичного ключа/уникальности.

Соответствующий индекс в любом случае нужен из соображений производительности.

Комментарий читателя от 2 апреля 2002 года

Спасибо, Том. Я знал, что если еще раз (внимательно) перечитаю твой ответ, то смогу найти ошибку в SQL-операторе. Но, в любом случае, не мог бы ты помочь мне добиться желанной цели - изменить три столбца из другой таблицы, имеющий один совпадающий столбец (object_name), уникальный, но не являющийся первичным ключом.

Ответ Тома Кайта

update t
  set ( f1, f2, f3 ) = 
        ( select a, b, c from t2 where t2.some_field = t.some_field )
 where exists ( select a, b, c from t2 where t2.some_field = t.some_field )
/

Этот запрос позволит решить вашу проблему. Если нельзя задать ограничение уникальности (которое нужно ТЕОРЕТИЧЕСКИ, даже если не потребуется практически - если значения в столбце some_field таблицы t2 окажутся НЕ уникальными, представленный запрос закончится сообщением об ошибке "подзапрос вернул более одной строки", потому что оператор update будет НЕОДНОЗНАЧНЫМ)

Хороший прием. Комментарий читателя от 9 апреля 2002 года

Спасибо за прекрасный сайт. Я пытаюсь изменить таблицу на основе определенной информации в той же таблице. Запрос выглядит следующим образом:

DECLARE
CURSOR c1 IS SELECT ROWID FROM
siebel.s_synd_data
    where
        WHERE
     ROW_ID LIKE 'DDM%'
     AND DATA_SRC_CD = 'DECPRF'
     and POSITION_ID IS NULL;
begin
for cur in c1 loop
     UPDATE SIEBEL.S_SYND_DATA A
     SET A.POSITION_ID = (SELECT B.ROW_ID FROM SIEBEL.S_POSTN B, 
SIEBEL.S_ASGN_GRP C
WHERE A.TERR_ID = C.ROW_ID
AND SUBSTR(C.NAME, 1, 5) // '00D' = B.NAME)
   where rowid=cur.rowid;
  if mod(c1%rowcount,50000)=0 then
           commit;
  end if;
end loop;
commit;
end ;

Предполагается, что условию WHERE курсора C1 будет удовлетворять 9 миллионов строк. Мой АБД говорит, что транзакция будет зафиксирована только после изменения 9 миллионов строк, и мне надо фиксировать ее после каждых 50000 - правильно ли это?

Что можно сделать, чтобы это изменение выполнялось быстрее - я знаю, что ты ненавидишь такие вопросы, если за ними не стоят определенные критерии, но что, если я создам индекс (по функции)?

Ответ Тома Кайта

Я лично делал бы так:

update s_synd_data a   -- извините, я не могу заставить себя набрать sie....
   set a.position_id = ( select b.row_id 
                           from s_postn b, s_asng_grp c
                          where c.row_id = a.terr_id
                            and b.name = substr(c.name,1,5) // '00D' )
 where row_id like 'DDM%'
   and data_src_cd = 'DECPRF'
   and position_id is null;

Если бы по столбцу position_id был индекс, я бы его удалил, выполнил изменение и пересоздал индекс без возможности восстановления и с распараллеливанием.

Прежде чем вы скажете: "но там же 9 миллионов строк - надо фиксировать по частям", я отвечу - ни в коем случае. Проверьте только, что выделено достаточно сегментов отката (иначе, при использовании вашего подхода, РАНО ИЛИ ПОЗДНО, вы получите сообщение об ошибке ORA-01555 - поищите на сайте обсуждения по слову 01555 или 1555 и поймете, что я имею ввиду).

Потребуется не так уж много места в сегментах отката, как может показаться. Процедурный код, вроде вашего:

  • будет работать медленне простого оператора update
  • сгенерирует больше данных отмены и повторного выполнения, чем один оператор update
  • будет источником ошибок (ora-01555, другие логические ошибки)
  • у вас уже есть логическая ошибка - c1%rowcount не содержит "суммарное" значение; это колчиество строк, измененных последним оператором update. Если вы измените фактически менее 50000 строк одним оператором, транзакция не будет зафиксирована (в вашем коде - ошибка)
  • вообще - плохая идея.

В своей системе я только что изменил 1099008 строк:

ops$tkyte@ORA817DEV.US.ORACLE.COM> update big_table set owner = lower(owner);
Elapsed: 00:08:38.66
ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk from v$transaction;

 USED_UBLK
----------
     13002
Elapsed: 00:00:00.01
ops$tkyte@ORA817DEV.US.ORACLE.COM> select 13002*8/1024 from dual;

13002*8/1024
------------
  101.578125
Elapsed: 00:00:00.01
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from big_table;

  COUNT(*)
----------
   1099008
Elapsed: 00:00:02.41

для этого потребовалось около 100 Мбайт в сегменте отката. Вам следует ожидать порядка гигабайта. Я бы просто убедился (с помощью АБД), что столько места есть, использовал бы оператор set transaction use rollback segment при необходимости (можно просто создать временный сегмент для этого оператора update в каталоге /tmp, например, и потом его удалить). Выполните update. Этого достаточно.

Меньше думать, меньше делать, меньше вероятность ошибки...

Комментарий читателя от 30 апреля 2002 года

Уважаемый Том...

В какой версии Oracle появилась возможность выполнять "изменение соединения"? Я всегда использовал для таких запросов второй вариант, с конструкцией EXISTS. Всегда казалось смешным, что приходится один и тот же подзапрос вводить дважды.

Тебе надо написать книжку о хитрых приемах в Oracle SQL.

Ответ Тома Кайта

Уверен, что эта возможность появилась в версии 7.3

ops$tkyte@ORA734.WORLD> create table t1 ( x int primary key, y int );

Table created.

ops$tkyte@ORA734.WORLD> create table t2 ( x int, y int );

Table created.

ops$tkyte@ORA734.WORLD> insert into t1 values ( 1, 1 );

1 row created.

ops$tkyte@ORA734.WORLD> insert into t1 values ( 2, 2 );

1 row created.

ops$tkyte@ORA734.WORLD> insert into t2 values ( 2, null );

1 row created.

ops$tkyte@ORA734.WORLD> insert into t2 values ( 3, null );

1 row created.

ops$tkyte@ORA734.WORLD> update ( select t1.y t1_y, t2.y t2_y
  2                     from t1, t2
  3                    where t1.x = t2.x )
  4     set t2_y = t1_y
  5  /

1 row updated.

ops$tkyte@ORA734.WORLD> select * from t2;

         X          Y
---------- ----------
         2          2
         3

Параллельные операторы ЯМД и изменение представления с соединением

ADHOC@VOYAGER> update 
  2  (select t.object_id toid,t1.object_id t1oid
  3  from t,t1
  4  where t.object_name=t1.object_name
  5  and upper(t.object_name)='PRIMARY_SALESFORCE')
  6  set toid=t1oid;  

В представленном выше запросе, который демонстрирует изменение представления с соединением, меня интересует, выполняется ли это изменение параллельно, если таблица t фрагментирована и выполнен оператор alter session enable parallel dml. Я использую аналогичный оператор update и выяснил, что лучшим планом выполнения оператора select будет полный просмотр обеих таблиц и соедитнение хешированием.

Ответ Тома Кайта

Если удовлетворены все остальные критерии (включено распараллеливание, таблицы допускают "распараллеливание") - должно выполняться параллельно.

Изменение будет выполняться по фрагментам таблицы T, с полным просмотром каждого фрагмента T и соединением его с таблицей T1 после фильтрации по условию upper(t.object_name).

Один из способов убедиться в этом - выполнить изменение, а затем - оператор select * from t where rownum = 1; Если этот оператор сработает, изменение не распараллеливалось, а если нет - было выполнено параллельно. Выполните Commit и сможете выбирать данные.

Можно ли это сделать, если задействовано более двух таблиц?

Я делал это, когда в соединении задействовано две таблицы.

Можно ли это сделать, если необходимо изменить столбец таблицы A в соответствии со значениями столбца в таблице C. Таблицы связаны так: A -> B -> C, где A является главной для всех, а B - главной для C?

Ответ Тома Кайта

Ай-я-яй, такой простой пример - вы же все уже указали - и не попытаться сделать... Ладно.

Ответ - да, если соблюдается "защита ключом", другими словами, каждая строка таблицы A появится в результате соединения НЕ БОЛЕЕ ОДНОГО РАЗА. В противном случае - нет.

Рассмотрим простой пример:

ops$tkyte@ORA920.LOCALHOSTgt; create table a ( x int primary key, y int );

Table created.

ops$tkyte@ORA920.LOCALHOST> create table b ( x references a primary key );

Table created.

ops$tkyte@ORA920.LOCALHOST> create table c ( x references b primary key, y int );

Table created.

ops$tkyte@ORA920.LOCALHOST> insert into a values ( 1, null );

1 row created.

ops$tkyte@ORA920.LOCALHOST> insert into a values ( 2, null );

1 row created.

ops$tkyte@ORA920.LOCALHOST> insert into b values ( 1 );

1 row created.

ops$tkyte@ORA920.LOCALHOST> insert into c values ( 1, 100 );

1 row created.

ops$tkyte@ORA920.LOCALHOST> select * from a;

         X          Y
---------- ----------
         1
         2

ops$tkyte@ORA920.LOCALHOST> update ( select a.y a_y, c.y c_y
  2          from a, b, c
  3             where a.x = b.x and b.x = c.x )
  4      set a_y = c_y;

1 row updated.

ops$tkyte@ORA920.LOCALHOST> select * from a;

         X          Y
---------- ----------
         1        100
         2

Блокировки. Комментарий читателя от 2 октября 2002 года

Том,

В случае:

update
      ( select columnName, value
          from name, lookup
         where name.keyname = lookup.keyname
           and lookup.otherColumn = :other_value )
       set columnName = value
    /

будут ли обе таблицы, name и lookup, заблокированы по ходу изменения?

Ответ Тома Кайта

ops$tkyte@ORA920.LOCALHOST> alter table emp add dname varchar2(30);

Table altered.

ops$tkyte@ORA920.LOCALHOST> update ( select emp.dname edname, dept.dname ddname
  2          from emp, dept
  3                where emp.deptno = dept.deptno )
  4   set edname = ddname;

14 rows updated.

ops$tkyte@ORA920.LOCALHOST> select type, id1, (select object_name from 
user_objects where object_id = id1) oname
  2    from v$lock
  3   where sid = (select sid from v$mystat where rownum=1)
  4      and type = 'TM'
  5  /

TY        ID1 ONAME
-- ---------- --------------------
TM      31715 EMP

Блокируются только строки таблицы, которую вы изменяете.

Комментарий читателя от 29 января 2003 года

scott@ORA734.WORLD> update
  2    ( select columnName, value
  3        from name, lookup
  4       where name.keyname = lookup.keyname
  5         and lookup.otherColumn = :other_value )
  6     set columnName = value
  7  /

1 row updated.

scott@ORA734.WORLD> update name
  2     set columnName = ( select value
  3                        from lookup
  4                       where lookup.keyname = name.keyname
  5                           and otherColumn = :other_value )
  6   where exists ( select value
  7                    from lookup
  8                   where lookup.keyname = name.keyname
  9                     and otherColumn = :other_value )
 10  /

1 row updated.

Том,

какой из этих двух запросов лучше с точки зрения производительности? Я предполагаю, что первый, но сомневаюсь. Если я ошибаюсь, то почему?

Ответ Тома Кайта

Оба плохи...

Нет, первый лучше

Хотя, постойте, второй лучше

Оба могут работать быстро, я думаю...

Их производительность будет зависеть от используемого оптимизатора, актуальности статистической информации, и т.п.

В общем случае, если "NAME" - очень маленькая таблица, а "LOOKUP" - очень большая, (но проиндексированная по keyname/othercolumn) - оператор с where exists вполне можно использовать.

Но обычно лучше изменять соединение.

Изменение главной таблицы на основе подчиненной (подчиненная - с составным ключом)

Не мог бы ты помочь мне сделать то, что я пытаюсь - изменить 2 или более столбца главной таблицы на основе данных в подчиненной, у которой первичный ключ - составной.

Пример:

Таблица: Food
(food_id    number primary key
 food_name  varchar2(100)
 calorie    number 
 fibre      number
 total_fat  number
)

Пример данных в таблице Food:
Food_id  food_name  calorie    fibre    total_fat
1        apple      null       null    null
2        beer       null       null    null

Таблица: Food_Portion
(food_id     number
 portion_id  number 
 qty         number
 weight      number
 calorie     number 
 fibre       number
 total_fat   number
 ) 
 
food_id + portion_id - первичный ключ

Пример данных в таблице Food_Portion:
Food_id    Portion_ID qty    weight calorie    fibre  total_fat
1          1          1      10     10         3      3    
1          2          2      15     20         5      2
1          3          100    100    60         10     9
2          1          1      10     5          0      0
2          2          .5     6      4          0      0
2          3          100    100    40         0      10

В результате изменения надо установить значения столбцов calorie, fibre и total_fat в таблице food_table равными значениям в таблице food_portion, где food_portion.qty = 100 и food_portion.weight = 100

Гарантируется, что для каждого food_id в таблице food_portion будет только одна строка с qty = 100 и weight = 100 (т.е. оператор select food_id from food_portion where qty = 100 and weight = 100 вернет ровно одну строку для каждого значения food_id)

Я выполнял следующий оператор update:

update ( select a1, b1 from a, b where a.id = b.id )
set a1 = b1 (this one I haven't tried)

Но получил сообщение об ошибке 'ORA-01779: cannot modify a column which maps to a non key-preserved table'

Потом я попробовал выполнить:

update t
  set ( f1, f2, f3 ) = 
        ( select a, b, c from t2 where t2.some_field = t.some_field )
 where exists ( select a, b, c from t2 where t2.some_field = t.some_field )
 
update a
set a1 = ( select b1 from b where b.id = a.id )
   where a.id in ( select id from b )

И оба оператора вернули сообщение об ошибке ORA-01427: single-row subquery returns more than one row

Меня интересует, можно ли выполнить такое изменение, не используя PL/SQL?

Ответ Тома Кайта

Если вы указываете:

where t2.some_field = t.some_field )

И получаете:

ORA-01427: single-row subquery returns more than one row

Значит, предположение о единственности строки для каждого food_id не оправдалось.

Но, в любом случае, если запрос:

select food_id
  from food_portion
 where qty = 100 and weight = 100
 group by food_id
 having count(*) > 1;

возвращает НОЛЬ строк (нет дублирующихся значений!), то:

ops$tkyte@ORA817DEV> update food
  2     set (calorie,fibre,total_fat) =
  3             (select calorie,fibre,total_fat
  4                    from food_portion fp
  5                   where fp.food_id = food.food_id
  6                     and fp.qty = 100
  7                     and fp.weight = 100 )
  8   where exists
  9             (select calorie,fibre,total_fat
 10                    from food_portion fp
 11                   where fp.food_id = food.food_id
 12                     and fp.qty = 100
 13                     and fp.weight = 100 )
 14  /

Выполняет соответствующее изменение.

Очень полезный пример

После того, как увидел способ изменения соединения, обнаружил, что он хорошо подходит для изменения одного источника данных с просмотром предварительных и окончательных значений:

scott@MSORA920>create table dept_demo as select * from dept;

Table created.

scott@MSORA920>SELECT deptno, dname, INITCAP(dname) Iname, loc, INITCAP(loc) 
Iloc FROM dept_demo;

    DEPTNO DNAME          INAME          LOC           ILOC
---------- -------------- -------------- ------------- -------------
        10 ACCOUNTING     Accounting     NEW YORK      New York
        20 RESEARCH       Research       DALLAS        Dallas
        30 SALES          Sales          CHICAGO       Chicago
        40 OPERATIONS     Operations     BOSTON        Boston

scott@MSORA920>edit
Wrote file afiedt.buf

  1  UPDATE(
  2  SELECT deptno, dname, INITCAP(dname) Iname, loc, INITCAP(loc) Iloc FROM 
dept_demo
  3* ) SET dname = Iname, loc = Iloc
scott@MSORA920>/

4 rows updated.

scott@MSORA920>edit
Wrote file afiedt.buf

  1* SELECT deptno, dname, INITCAP(dname) Iname, loc, INITCAP(loc) Iloc FROM 
dept_demo
scott@MSORA920>/

    DEPTNO DNAME          INAME          LOC           ILOC
---------- -------------- -------------- ------------- -------------
        10 Accounting     Accounting     New York      New York
        20 Research       Research       Dallas        Dallas
        30 Sales          Sales          Chicago       Chicago
        40 Operations     Operations     Boston        Boston

scott@MSORA920>

Спасибо :)

Комментарий читателя от 1 мая 2003 года

Том,

Если в твоем исходном примере вместо:

Update...
SET    columnName = Value;

Написать:

Update...
SET    Value = columnName;

Я получаю сообщение об ошибке "ORA-01779: cannot modify a column which maps to a non key-preserved table" (см. ниже).

Разме в запросе columnName не возвращает тоже всего одну запись? Почему так не срабатывает?

SQL> CREATE TABLE name (
  2  keyname    INT,
  3  columnName VARCHAR2(25)
  4  );

Table created.

SQL> CREATE TABLE lookup (
  2  keyname     INT PRIMARY KEY,
  3  value       VARCHAR2(25),
  4  otherColumn INT
  5  );

Table created.

SQL> insert into name values ( 100, 'Original Data' );

1 row created.

SQL> insert into name values ( 200, 'Original Data' );

1 row created.

SQL> insert into lookup values ( 100, 'New Data', 1 );

1 row created.

SQL> commit;

Commit complete.

SQL> UPDATE (
  2     SELECT a.columnName, b.value
  3     FROM   name a, lookup b
  4     WHERE  a.keyname = b.keyname AND
  5            b.otherColumn = 1)
  6  SET columnName = value;

1 row updated.

SQL> rollback;

Rollback complete.

SQL> UPDATE (
  2     SELECT a.columnName, b.value
  3     FROM   name a, lookup b
  4     WHERE  a.keyname = b.keyname AND
  5            b.otherColumn = 1)
  6  SET value = columnName;
SET value = columnName
    *
ERROR at line 6:
ORA-01779: cannot modify a column which maps to a non key-preserved table

Ответ Тома Кайта

SQL> CREATE TABLE name (
  2  keyname    INT,
  3  columnName VARCHAR2(25)
  4  );

Table created.

SQL> CREATE TABLE lookup (
  2  keyname     INT PRIMARY KEY,
  3  value       VARCHAR2(25),
  4  otherColumn INT
  5  );

insert into name values ( 1, 'x' );
insert into name values ( 1, 'y' );

insert into lookup values ( 1, null, null );

А теперь, какой результат должен получиться после изменения:

SQL> UPDATE (
  2     SELECT a.columnName, b.value
  3     FROM   name a, lookup b
  4     WHERE  a.keyname = b.keyname AND
  5            b.otherColumn = 1)
  6  SET value = columnName;

Столбец value будет иметь значение x или y?

Чтобы избавиться от двусмысленности, необходим первичный ключ по столбцам таблиц "lookup", по которым выполняется соединение - у вас его нет.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 30.03.2010 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Oracle Database Standard Edition 2 Processor License
Oracle Database Personal Edition Named User Plus Software Update License & Support
Oracle Database Standard Edition 2 Named User Plus License
Oracle Database Personal Edition Named User Plus License
Quest Software. SQL Navigator Professional Edition
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
СУБД Oracle "с нуля"
Компьютерные книги. Рецензии и отзывы
Новые программы для Windows
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100