Cập nhật tuyên bố với tham gia bên trong trên Oracle


298

Tôi có một truy vấn hoạt động tốt trong MySQL, nhưng khi tôi chạy nó trên Oracle, tôi gặp lỗi sau:

Lỗi SQL: ORA-00933: Lệnh SQL chưa kết thúc đúng
00933. 00000 - "Lệnh SQL không kết thúc đúng cách"

Truy vấn là:

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

Khi tôi cố gắng thiết lập bảng2 trong Oracle để kiểm tra câu trả lời của mình, tôi thấy rằng Oracle đã từ chối DESC làm tên cột.
Janek Bogucki

Xin lỗi, tôi chỉ viết tắt tên cột ban đầu để giải thích rõ ràng là không phải trong db
user169743

Câu trả lời:


412

Cú pháp đó không hợp lệ trong Oracle. Bạn có thể làm được việc này:

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

Hoặc bạn thể làm điều này:

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

Nó phụ thuộc nếu chế độ xem nội tuyến được coi là có thể cập nhật bởi Oracle ( Để có thể cập nhật cho câu lệnh thứ hai phụ thuộc vào một số quy tắc được liệt kê ở đây ).


5
Tôi đã làm ví dụ thứ hai nhưng phải thêm bí danh vào tên cột trong phần chọn và sau đó tham chiếu chúng theo tên của chúng trong SET nhưng nó đã hoạt động, cảm ơn
Gustavo Rubio

41
Ví dụ thứ hai có lợi ích là cho phép bạn kiểm tra SQL trước khi thực sự cập nhật.
Daniel Reis

10
Ví dụ thứ hai làm việc cho tôi. Tôi thích cái đó vì nó trông sạch sẽ và dễ đọc. Không biết những ưu và nhược điểm giữa hai loại khi nói đến hiệu suất. Nhưng, tôi đã không lo lắng về điều đó vì bây giờ tôi đã sử dụng điều này cho một kịch bản tắt để sửa dữ liệu xấu.
nemo

5
Thứ hai làm việc cho tôi :). Oracle là một động vật mạnh mẽ nhưng kỳ lạ: /
elrado

10
Giải thích về yêu cầu bảo quản khóa cho các phép nối có thể cập nhật: asktom.oracle.com/pls/asktom/ mẹo
Vadzim 12/2/2015

202

Dùng cái này:

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
Hoạt động hoàn hảo nhưng Oracle yêu cầu tôi phải nói merge into table 1 tvà vân vân.
Michael-O

1
Đến bữa tiệc muộn, nhưng đây vẫn là một chủ đề tốt. Tôi cần biết, tho '... tôi có bỏ lỡ điều gì không? Bảng chính, "bảng1". Trong SỬ DỤNG, bảng1 được đặt bí danh là t1. Bảng 2, được đặt bí danh là t2, nhưng trong ON, các tham chiếu là ...? Bảng ngoài1 - không phải t1 - đây là tham chiếu đến bảng ngoài hay loại? Ban 2? Không phải t2? Je suis bối rối. Người hâm mộ của bí danh tốt hơn ...
Marc

Chỉ cần một điểm ở đây, nếu khóa của bạn (trg.rowid hoặc src.rid) có một mục trùng lặp thì mệnh đề này sẽ gây ra lỗi: ora-30926.ora-code.com
Henrique

@Marc Trong ON, trglà bí danh cho bảng chính, table1( bảng "bên ngoài" theo logic của bạn) và srctham chiếu USINGnhóm ("bảng bên trong" theo logic của bạn). Nhưng vâng, có lẽ đã được tham khảo tốt hơn, nhưng tôi đã có thể theo dõi nó.
vapcguy

1
@supernova: câu trả lời của tony đang cập nhật chế độ xem nội tuyến. Điều này có thể hoạt động trong một số trường hợp, nhưng chế độ xem phải được "bảo tồn khóa" (mọi bảng đã tham gia phải được nối bằng nhau trên khóa chính của nó hoặc bộ trường duy nhất khác). Điều này đảm bảo mọi bản ghi trong bảng đích đều đóng góp tối đa một bản ghi trong hàng kết quả và do đó, mọi bản ghi trong bảng đích được cập nhật nhiều nhất một lần.
Quassnoi

25

MERGEvới WHEREmệnh đề:

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;

Bạn cần WHEREmệnh đề vì các cột được tham chiếu trong ONmệnh đề không thể được cập nhật.


Phiên bản này được cho là sạch hơn, nhưng nó không kích hoạt thân thiện vì không có cách nào tôi biết để tránh kích hoạt cập nhật kích hoạt cho các hàng không thay đổi bằng cú pháp này. (Tôi giả sử rằng các kích hoạt là cần thiết cho các hàng đã thay đổi .)
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

Không sử dụng một số câu trả lời ở trên.

Một số gợi ý việc sử dụng CHỌN lồng nhau, đừng làm điều đó, nó rất chậm. Nếu bạn có nhiều hồ sơ để cập nhật, hãy sử dụng tham gia, vì vậy đại loại như:

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;

Xem liên kết này để biết thêm chi tiết. http://geekswithbloss.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Ngoài ra, đảm bảo rằng có các khóa chính trên tất cả các bảng bạn đang tham gia.


7

Như đã chỉ ra ở đây , cú pháp chung cho giải pháp đầu tiên được đề xuất bởi Tony Andrew là:

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
                         )

Tôi nghĩ rằng điều này là thú vị đặc biệt nếu bạn muốn cập nhật nhiều hơn một lĩnh vực.


Điều này không làm việc cho tôi. Nó cập nhật toàn bộ bảng.
Natassia Tavares

3

Cú pháp sau đây làm việc cho tôi.

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 Vui lòng chỉnh sửa lại câu trả lời này để tôi có thể xóa downvote của mình .... Tôi đã cố gắng sử dụng cú pháp này và nó không cập nhật bảng của tôi. Tôi đã tìm ra lý do tại sao - tôi SETđang làm REPLACEvà tôi đang cố gắng xóa một chuỗi cụ thể trong cột - hóa ra Oracle coi ''là null và trường này không thể bị hủy. Tôi nghĩ rằng cú pháp chỉ đơn thuần là cập nhật một bảng tạm thời thay vì bảng thực, nhưng tôi đã sai.
vapcguy

2

Sử dụng mô tả thay vì desc cho bảng2,

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

tại sao bạn muốn bắn hai truy vấn riêng biệt trên bảng2
Jitendra Vispute

2

Nó hoạt động tốt

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

Có thể đặt nhiều thuộc tính bằng cách thêm dấu phẩy vào cuối đó. Tôi cần phải thực hiện t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNametrên một bảng sau khi khớp nó trên cột "Tên người dùng" ( t1.UserName = t2.UserName) để lấy tên của họ từ một bảng có tên UserInfo ( select * from UserInfo) t2). Cơ sở dữ liệu là nơi nó đang sử dụng UserName làm khóa chính cho UserInfo ở mọi nơi, thay vì đặt FirstName và LastName trong bảng, trực tiếp. Cái này đã sửa mà!
vapcguy

Câu trả lời này không thêm gì vào câu trả lời đã được Quassnoi cung cấp năm năm trước bạn.
Thức ăn gia súc

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

Cũng giống như vấn đề hoàn chỉnh, và vì chúng ta đang nói về Oracle, điều này cũng có thể làm điều đó:

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
Điều này có thể làm điều đó, nhưng đó là về cách chậm nhất có thể.
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 và B là các trường bí danh, bạn không cần phải trỏ bảng.


1
Chào Dân. Bạn đang đăng lên một câu hỏi khá cũ mà đã có câu trả lời rất tốt. Bạn có thể giải thích khi câu hỏi của bạn là thích hợp hơn các giải pháp khác?
Noel Widmer

1
Tất nhiên, tôi đã thấy một câu trả lời trong đó b = a được viết bằng cách chỉ tên bảng (bảng1.B = bảng2.A) nhưng không cần phải chỉ ra bảng.
Dan Anderson

Bạn đang thực sự cập nhật các trường từ chế độ xem, được ánh xạ tới bảng. Nếu chế độ xem bên trong được đặt bí danh h, thì phiên bản "tự ghi lại" sẽ là "set hb = ha".
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
             )
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.