Oracle'da dahili birleştirmeyle güncelleme bildirimi


298

MySQL iyi çalışan bir sorgu var, ama Oracle üzerinde çalıştırdığınızda aşağıdaki hatayı alıyorum:

SQL Hatası: ORA-00933: SQL komutu düzgün bir şekilde sona
ermedi 00933. 00000 - "SQL komutu düzgün bir şekilde sona ermedi"

Sorgu:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';

Cevabımı test etmek için Oracle'da table2 kurmaya çalıştığımda Oracle'ın DESC'yi bir sütun adı olarak reddettiğini gördüm.
Janek Bogucki

Üzgünüz, sadece db değil açıkçası desc için orijinal sütun adını kısaltılmış
user169743 15:10

Yanıtlar:


412

Bu sözdizimi Oracle için geçerli değildir. Bunu yapabilirsiniz:

UPDATE table1 SET table1.value = (SELECT table2.CODE
                                  FROM table2 
                                  WHERE table1.value = table2.DESC)
WHERE table1.UPDATETYPE='blah'
AND EXISTS (SELECT table2.CODE
            FROM table2 
            WHERE table1.value = table2.DESC);

Yoksa belki bunu yapmak mümkün:

UPDATE 
(SELECT table1.value as OLD, table2.CODE as NEW
 FROM table1
 INNER JOIN table2
 ON table1.value = table2.DESC
 WHERE table1.UPDATETYPE='blah'
) t
SET t.OLD = t.NEW

Satır içi görünümün Oracle tarafından güncellenebilir olarak kabul edilip edilmeyeceğine bağlıdır (İkinci ifade için güncellenebilir olması, burada listelenen bazı kurallara bağlıdır ).


5
İkinci örneği yaptım ancak seçimdeki sütun adlarına takma adlar eklemek ve ardından SET'teki adlarına göre onları referans etmek zorunda kaldım, ancak işe yaradı, teşekkürler
Gustavo Rubio

41
İkinci örnek, güncellemeyi gerçekleştirmeden önce SQL'i test etmenize izin verme avantajına sahiptir.
Daniel Reis

10
İkinci örnek benim için çalıştı. Bunu beğendim çünkü temiz ve okunabilir görünüyor. Performans söz konusu olduğunda ikisi arasında artıları ve eksileri neler olduğunu bilmiyorum. Ama bunun için endişelenmedim çünkü kötü verileri düzeltmek için bunu bir kerelik bir senaryo için kullandım.
nemo

5
İkinci benim için çalıştı :). Oracle güçlü ama garip bir hayvandır: /
elrado


202

Bunu kullan:

MERGE
INTO    table1 trg
USING   (
        SELECT  t1.rowid AS rid, t2.code
        FROM    table1 t1
        JOIN    table2 t2
        ON      table1.value = table2.DESC
        WHERE   table1.UPDATETYPE='blah'
        ) src
ON      (trg.rowid = src.rid)
WHEN MATCHED THEN UPDATE
    SET trg.value = code;

2
Mükemmel çalışıyor ancak Oracle söylememi istedi merge into table 1 t.
Michael-O

1
Partiye geç, ama bu hala iyi bir iş parçacığı. Bilmem gerek, tho ... ... bir şey mi kaçırdım? Ana tablo, "tablo1". KULLANIMDA, table1 t1 olarak adlandırılmıştır. Tablo2, t2 olarak diğer ad olarak adlandırıldı, ancak AÇIK durumda, başvurular ...? Dış Tablo1 - t1 değil - bu dış tabloya mı yoksa bir türe mi başvuruyor? Tablo 2? T2 değil mi? Je suis kafası karıştı. Daha iyi takma adlar ...
Marc

Burada sadece bir nokta, anahtarınızda (trg.rowid veya src.rid) tek bir öğe varsa, bu madde bir hata verir: ora-30926.ora-code.com
Henrique

@Marc olarak ON, trgana tablo için takma olan table1(mantık ile "dış" tablo), ve srcreferansları USING(mantık "bir iç tablo") grubudur. Ama evet, muhtemelen daha iyi referans gösterilebilirdi, ama takip edebildim.
vapcguy

1
@supernova: tony'nin yanıtı satır içi görünümü güncelliyor. Bu, bazı durumlarda işe yarayabilir, ancak görünümün "anahtar korumalı" olması gerekir (birleştirilmiş her tablo, birincil anahtarında veya başka bir şekilde benzersiz alan kümesinde eşitlikle birleştirilmelidir). Bu, hedef tablodaki her kaydın sonuç satır kümesinde en fazla bir kayda katkıda bulunmasını ve dolayısıyla hedef tablodaki her kaydın en fazla bir kez güncelleştirilmesini sağlar.
Quassnoi

25

MERGEile WHEREfıkra:

MERGE into table1
USING table2
ON (table1.id = table2.id)
WHEN MATCHED THEN UPDATE SET table1.startdate = table2.start_date
WHERE table1.startdate > table2.start_date;

İhtiyacınız WHEREbaşvurulan sütunlar nedeniyle maddesini ONmaddesinin güncellenmiş edilemez.


Bu sürüm tartışmasız daha temiz, ancak tetikleyici dostu değil, çünkü bu sözdizimini kullanarak değişmemiş satırlar için güncelleme tetikleyicilerini tetiklemekten kaçınmanın bir yolu yok. (Tetikleyicilerin değiştirilen satırlar için gerekli olduğunu varsayıyorum .)
sf_jeff

14
 UPDATE ( SELECT t1.value, t2.CODE
          FROM table1 t1
          INNER JOIN table2 t2 ON t1.Value = t2.DESC
          WHERE t1.UPDATETYPE='blah')
 SET t1.Value= t2.CODE

11

Yukarıdaki cevaplardan bazılarını kullanmayın.

Bazıları iç içe SELECT kullanımını önerir, bunu yapma, son derece yavaştır. Güncellenecek çok fazla kaydınız varsa, join özelliğini kullanın;

update (select bonus 
        from employee_bonus b 
        inner join employees e on b.employee_id = e.employee_id 
        where e.bonus_eligible = 'N') t
set t.bonus = 0;

Daha fazla ayrıntı için bu bağlantıya bakın. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Ayrıca, katıldığınız tüm tablolarda birincil anahtarların bulunduğundan emin olun.


7

Belirtildiği gibi burada Tony Andrews tarafından önerilen ilk çözüm için genel sözdizimi şöyledir:

update some_table s
set   (s.col1, s.col2) = (select x.col1, x.col2
                          from   other_table x
                          where  x.key_value = s.key_value
                         )
where exists             (select 1
                          from   other_table x
                          where  x.key_value = s.key_value
                         )

Özellikle birden fazla alanı güncellemek istiyorsanız bunun ilginç olduğunu düşünüyorum.


Bu benim için işe yaramıyor. Tüm tabloyu günceller.
Natassia Tavares

3

Aşağıdaki sözdizimi benim için çalışıyor.

UPDATE
(SELECT A.utl_id,
    b.utl1_id
    FROM trb_pi_joint A
    JOIN trb_tpr B
    ON A.tp_id=B.tp_id Where A.pij_type=2 and a.utl_id is null
)
SET utl_id=utl1_id;

@JimGarrison Downvote'umu kaldırabilmem için lütfen bu cevabı tekrar düzenleyin .... Bu sözdizimini kullanmaya çalışıyordum ve masamı güncellemiyordu. Neden - benim SETyapıyordum REPLACEve sütundaki belirli bir dizeyi boşaltmaya çalışıyordum - Oracle'ın ''null olduğunu kabul ediyor ve bu alan boş bırakılamadı. Sözdiziminin sadece gerçek olanın yerine geçici bir tabloyu güncellediğini düşündüm, ama yanılmışım.
vapcguy

2

Table2 için desc yerine açıklama kullanarak ,

update
  table1
set
  value = (select code from table2 where description = table1.value)
where
  exists (select 1 from table2 where description = table1.value)
  and
  table1.updatetype = 'blah'
;

neden u table2 üzerinde iki ayrı sorgu ateşlemek istiyorum
Jitendra Vispute

2

İyi çalışır oracle

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary

Bunun sonuna virgül ekleyerek birden fazla özellik ayarlayabilirsiniz. UserInfo ( ) adlı bir tablodan adlarını almak t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNameiçin "UserName" sütununda ( t1.UserName = t2.UserName) eşleştirdikten sonra bir tablo üzerinde yapmak gerekiyordu select * from UserInfo) t2. Veritabanı, FirstName ve LastName'i doğrudan tabloya yerleştirmek yerine her yerde UserInfo için birincil anahtar olarak UserName'i kullanıyordu. Bu düzeltildi!
vapcguy

Bu cevap, sizden beş yıl önce Quassnoi tarafından verilen cevaba hiçbir şey katmıyor.
Yem

0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';

0
UPDATE IP_ADMISSION_REQUEST ip1
SET IP1.WRIST_BAND_PRINT_STATUS=0
WHERE IP1.IP_ADM_REQ_ID        =
  (SELECT IP.IP_ADM_REQ_ID
  FROM IP_ADMISSION_REQUEST ip
  INNER JOIN VISIT v
  ON ip.ip_visit_id=v.visit_id
  AND v.pat_id     =3702
  ); `enter code here`

0

Bir bütünlük meselesi olarak ve Oracle'dan bahsettiğimiz için, bunu da yapabilirdi:

declare
begin
  for sel in (
    select table2.code, table2.desc
    from table1
    join table2 on table1.value = table2.desc
    where table1.updatetype = 'blah'
  ) loop
    update table1 
    set table1.value = sel.code
    where table1.updatetype = 'blah' and table1.value = sel.desc;    
  end loop;
end;
/

1
Bu yapabilirdi, ama mümkün olan en yavaş yolla ilgili.
APC

-1
UPDATE (SELECT T.FIELD A, S.FIELD B
FROM TABLE_T T INNER JOIN TABLE_S S
ON T.ID = S.ID)
SET B = A;

A ve B diğer ad alanlarıdır, tabloyu göstermenize gerek yoktur.


1
Merhaba Dan. Zaten çok iyi cevapları olan oldukça eski bir soru yayınlıyorsunuz. Sorunun diğer çözümlere göre ne zaman tercih edildiğini açıklayabilir misiniz?
Noel Widmer

1
Tabii ki, b = a'nın tablo adını (table1.B = table2.A) işaret ederek yazıldığı bir cevap gördüm, ancak tabloyu göstermeye gerek yok.
Dan Anderson

Aslında, tablodan eşlenen görünümden alanları güncelleştiriyorsunuz. İç görünüm h olarak adlandırılmışsa, "kendi kendini belgeleyen" sürüm "hb = ha olarak ayarlanmış" olur.
sf_jeff

-4
update table1  a 
   set a.col1='Y' 
 where exists(select 1 
                from table2 b
               where a.col1=b.col1 
                 and a.col2=b.col2
             )
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.