web-dev-qa-db-ja.com

別のテーブルの値で列を更新する

users(列emailnameid)とuser_details(列usernameを含む)の2つのテーブルがあります。 、addressdetails)。現在、user_detailsのユーザー名には、IDとメールの組み合わせが入力されています。テーブルにすべてのIDが必要です。現在電子メールが入力されているユーザーの詳細の行を、ユーザーテーブルの同じ電子メールの行と照合し、その行のIDで更新する方法はありますか?以下のクエリを試しました:

update 
(select distinct
    u.sid as new_id,
    ud.username as id
from user_details ud
    inner join users u on
        lower(ud.username) = u.email) up set up.id = up.new_id;

2つのテーブルを結合してから結合を更新しようとすると、次のエラーが発生します。

SQLエラー:ORA-01779:非キー保存テーブル01779にマップする列は変更できません。00000-"非キー保存テーブルにマップする列は変更できません"

2
Sasha M

説明している状況に似た状況を作成しようとするとき、Oracle 12cを使用して、次のDDLコード(制約がないことに注意)を使用しました。

テスト設定

create table users (
  email varchar2(32)
, name varchar2(32)
, sid varchar2(32) -- <- assumption: data type NOT number
);

create table user_details (
  username varchar2(32)  -- <- populated with a mix of ids and emails
, address varchar2(64)
, details varchar2(64)
); 

テストデータを挿入すると、テーブルには...

SQL> select * from users;
EMAIL                  NAME    SID  
[email protected]  name_1  1    
[email protected]  name_2  2    
[email protected]  name_3  3  

-- user_details: username contains "a mix of ID's and Email's"
SQL> select * from user_details;
USERNAME               ADDRESS    DETAILS                     
1                      address_1  details_1                   
[email protected]             more details (user 1)       
2                      address_2  details_2                   
[email protected]             more details (user 2)       
3                      address_3  details_3                   
[email protected]             more details (user 3)       
3                      ---        some more details (user 3)  

問題

オリジナルのUPDATEを使用すると、「ORA-01732」が取得されます

update 
(select distinct
    u.sid as new_id,
    ud.username as id
from user_details ud
    inner join users u on
        lower(ud.username) = u.email) up set up.id = up.new_id ;
Error at Command Line : 2 Column : 1
Error report -
SQL Error: ORA-01732: data manipulation operation not legal on this view
01732. 00000 -  "data manipulation operation not legal on this view"

DISTINCTなしで同じUPDATEを実行すると、「ORA-01779」が得られます

update ( 
  select 
    u.sid as new_id,
    ud.username as id
  from user_details ud
    inner join users u on lower(ud.username) = u.email
) up 
set up.id = up.new_id ;
SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
01779. 00000 -  "cannot modify a column which maps to a non key-preserved table"
*Cause:    An attempt was made to insert or update columns of a join view which
           map to a non-key-preserved table.
*Action:   Modify the underlying base tables directly.

SETのサブクエリでこれを実行できるかどうかを見てみましょう...(まだ喜びはありません)

UPDATE user_details 
SET user_details.username = 
(
    SELECT DISTINCT users.sid AS new_id
    FROM users, user_details
    WHERE LOWER(user_details.username) = users.email
);
Error report -
ORA-01427: single-row subquery returns more than one row

ソリューション

たぶん、次の解決策を検討したいかもしれません:元のuser_detailsテーブルをそのままにしておきます。 user_detailsから必要なものをすべて選択して、新しいテーブルを作成します。

create table userdetails_new
as
select 
  u.sid       as new_id -- <- sid corresponds to an email address in users
, ud.address  as address
, ud.details  as details
from user_details ud
  join users u on lower(ud.username) = u.email
union
select
  username -- <- username that is NOT an email address
, address
, details
from user_details
where username not like '%@%' ;

新しい「ユーザーの詳細」テーブルには、...

SQL> select * from userdetails_new;
NEW_ID  ADDRESS    DETAILS                     
1                  more details (user 1)       
1       address_1  details_1                   
2                  more details (user 2)       
2       address_2  details_2                   
3                  more details (user 3)       
3                  some more details (user 3)  
3       address_3  details_3  

ここで、次のような制約を追加することもできます。

alter table users add unique(sid);

alter table userdetails_new
add constraint fkey_userdetails
foreign key (new_id) references users (sid) ;

これには他の解決策があると確信しています。ただし、推奨される手順により、必要な限り、つまり更新/変換が正常に行われたことを確認するまで、元の「user_details」データを保持できます。 Dbfiddle here。

2
stefan

テーブルを更新するように指示する必要がありますが、現在はクエリを更新するように指示しています

UPDATE user_details 
SET user_details.id = 
(
    SELECT DISTINCT users.sid AS new_id
    FROM users 
    WHERE LOWER(user_details.username) = users.email)
);
1
indiri