Oracle SQL: Cập nhật bảng có dữ liệu từ bảng khác


251

Bảng 1:

id    name    desc
-----------------------
1     a       abc
2     b       def
3     c       adf

Ban 2:

id    name    desc
-----------------------
1     x       123
2     y       345

Trong oracle SQL, làm cách nào để chạy truy vấn cập nhật sql có thể cập nhật Bảng 1 với Bảng 2 namedescsử dụng tương tự id? Vì vậy, kết quả cuối cùng tôi sẽ nhận được là

Bảng 1:

id    name    desc
-----------------------
1     x       123
2     y       345
3     c       adf

Câu hỏi được lấy từ bản cập nhật một bảng với dữ liệu từ bảng khác , nhưng cụ thể là đối với SQL tiên tri.



Bạn cần quay lại câu hỏi khác của mình, không chấp nhận câu trả lời đó và nói rõ rằng bạn cần cú pháp Oracle PLQuery.
p.campbell

3
@ p.campbell, Đó không phải là câu hỏi của tôi ...
Muhd

1
Ồ tôi hiểu rồi. Vì vậy, bạn sao chép-dán cơ thể câu hỏi, nhưng sửa đổi để bao gồm bit Oracle.
p.campbell

2
Vâng. Và đây có lẽ không phải là ví dụ tốt nhất vì "desc" là một từ dành riêng, nhưng ồ.
Muhd

Câu trả lời:


512

Đây được gọi là cập nhật tương quan

UPDATE table1 t1
   SET (name, desc) = (SELECT t2.name, t2.desc
                         FROM table2 t2
                        WHERE t1.id = t2.id)
 WHERE EXISTS (
    SELECT 1
      FROM table2 t2
     WHERE t1.id = t2.id )

Giả sử kết quả tham gia trong chế độ xem được bảo tồn khóa, bạn cũng có thể

UPDATE (SELECT t1.id, 
               t1.name name1,
               t1.desc desc1,
               t2.name name2,
               t2.desc desc2
          FROM table1 t1,
               table2 t2
         WHERE t1.id = t2.id)
   SET name1 = name2,
       desc1 = desc2

8
Trong ví dụ mã đầu tiên của bạn: Mệnh đề WHERE bên ngoài có cần thiết cho kết quả chính xác không? Hay bạn chỉ sử dụng nó để tăng tốc truy vấn?
Mathias Bader

41
@totoro - Trong ví dụ đầu tiên, WHERE EXISTSngăn bạn cập nhật một hàng trong t1nếu không có hàng phù hợp trong t2. Không có nó, mọi hàng trong t1sẽ được cập nhật và các giá trị sẽ được đặt thành NULLnếu không có hàng phù hợp t2. Đó thường không phải là những gì bạn muốn xảy ra vì vậy WHERE EXISTSthường là cần thiết.
Hang Justin

3
Đáng để thêm rằng SELECT ... FROM t2 phải dẫn đến một hàng duy nhất. Điều này có nghĩa là bạn phải chọn trên tất cả các trường bao gồm một khóa duy nhất - một khóa chính không duy nhất là không đủ. Nếu không có tính duy nhất, bạn bị giảm xuống thành một thứ như vòng lặp của @ PaulKarr - và nếu không có mối tương quan duy nhất, thì có thể cập nhật nhiều hơn một hàng mục tiêu cho mỗi hàng nguồn.
Andrew Leach

2
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

1
@RachitSharma - Điều đó có nghĩa là truy vấn con của bạn (truy vấn từ table2) đang trả về nhiều hàng cho một hoặc nhiều table1giá trị và Oracle không biết bạn muốn sử dụng cái nào. Thông thường, điều đó có nghĩa là bạn cần tinh chỉnh truy vấn con để nó trả về một hàng riêng biệt.
Hang động Justin

132

Thử cái này:

MERGE INTO table1 t1
USING
(
-- For more complicated queries you can use WITH clause here
SELECT * FROM table2
)t2
ON(t1.id = t2.id)
WHEN MATCHED THEN UPDATE SET
t1.name = t2.name,
t1.desc = t2.desc;

4
Thực sự rất nhanh, 1159477 hàng đã hợp nhất trong 15,5 giây
jefissu

3
Tôi hy vọng tất cả mọi người truy cập câu hỏi này sau năm 2015 thông báo câu trả lời này. Lưu ý rằng điều này cũng hoạt động nếu table1table2là cùng một bảng, chỉ cần quan tâm đến ONphần và phần WHERE-cách cho -statement SELECTcủa table2!
sjngm

1
Tôi thấy rằng mỗi khi tôi cần thực hiện một sự hợp nhất khác, tôi tiếp tục quay lại câu trả lời này để lấy cảm hứng. Tôi có thể in nó ra và đóng khung nó lên tường của tôi
arnehehe

Hoạt động như quyến rũ !! Cám ơn!
davidwillianx

CHỌN ID DISTINCT, FIELD1, FIELD1 TỪ bảng2 ID KHÔNG Ở ĐÂU
Joseph Poirier

17

thử

UPDATE Table1 T1 SET
T1.name = (SELECT T2.name FROM Table2 T2 WHERE T2.id = T1.id),
T1.desc = (SELECT T2.desc FROM Table2 T2 WHERE T2.id = T1.id)
WHERE T1.id IN (SELECT T2.id FROM Table2 T2 WHERE T2.id = T1.id);

4
Nhược điểm của điều này là câu lệnh SELECT được lặp lại 3 lần. Trong các ví dụ phức tạp có thể là một công cụ thỏa thuận.
David Balažic

9
Update table set column = (select...)

không bao giờ làm việc cho tôi vì thiết lập chỉ mong đợi 1 giá trị - Lỗi SQL: ORA-01427: truy vấn con một hàng trả về nhiều hơn một hàng.

đây là giải pháp:

BEGIN
For i in (select id, name, desc from table1) 
LOOP
Update table2 set name = i.name, desc = i.desc where id = i.id;
END LOOP;
END;

Đó chính xác là cách bạn chạy nó trên trang tính SQLDeveloper. Họ nói rằng nó chậm nhưng đó là giải pháp duy nhất phù hợp với tôi trong trường hợp này.


ai đó có thể vui lòng giải thích tại sao điều này xứng đáng với danh tiếng -2? CƯỜI LỚN.
Pau Karr

13
Tôi đã không giảm giá, nhưng nó không phải là một giải pháp tốt. Thứ nhất: nếu subselect trả về nhiều giá trị, thì vòng lặp for sẽ ghi đè tên trên bảng2 nhiều lần cho một số / tất cả các bản ghi (không sạch). Thứ hai: không có thứ tự theo mệnh đề nên điều này sẽ xảy ra theo cách không thể đoán trước (tức là giá trị cuối cùng trong chiến thắng dữ liệu không có thứ tự). Thứ ba: Sẽ chậm hơn nhiều. Giả sử kết quả của vòng lặp for đã được dự định, phần phụ ban đầu có thể đã được viết lại theo một cách được kiểm soát nào đó để chỉ trả về 1 giá trị cho mỗi bản ghi ... cách đơn giản nhất sẽ là (chọn min (name) ...)
Alternator

Đây chính xác là những gì tôi cần. Cảm ơn (+1)
Robert Hyatt

3
Nếu bạn nhận được nhiều giá trị trong truy vấn con của mình, bạn có thể suy nghĩ lại truy vấn và sử dụng DISTINCT hoặc GROUP BY với MIN, MAX. Chỉ là một ý tưởng.
Phanxicô

Câu chuyện dài: nếu bạn hoàn toàn có thể tránh nó, thì đừng bao giờ sử dụng bất kỳ loại LOOP nào trong câu lệnh T-SQL. Cá nhân, nếu nó không dành cho 0,001% thời gian không có giải pháp nào khác, tôi thậm chí không nghĩ rằng nó thậm chí còn là một chức năng có sẵn trong T-SQL. T-SQL được thiết kế dựa trên tập hợp, do đó, nó hoạt động trên toàn bộ tập hợp dữ liệu; nó KHÔNG nên được sử dụng để làm việc trên từng dòng dữ liệu.
Ray K.

8

Đây dường như là một câu trả lời thậm chí tốt hơn với mệnh đề 'in' cho phép nhiều khóa cho phép nối :

update fp_active set STATE='E', 
   LAST_DATE_MAJ = sysdate where (client,code) in (select (client,code) from fp_detail
  where valid = 1) ...

Thịt bò có các cột mà bạn muốn sử dụng làm khóa trong ngoặc đơn trong mệnh đề where trước 'in' và có câu lệnh chọn có cùng tên cột trong ngoặc đơn. trong đó ( cột1, cột2 ) trong ( chọn ( cột1, cột2 ) từ bảng trong đó "tập hợp tôi muốn" );


Liên kết đã hết hạn. ( 404)
Dumbo

-3

Nếu bảng t1 của bạn và đó là bản sao lưu t2 có nhiều cột, đây là một cách nhỏ gọn để làm điều đó.

Ngoài ra, vấn đề liên quan của tôi là chỉ một số cột được sửa đổi và nhiều hàng không có chỉnh sửa cho các cột này, vì vậy tôi muốn để chúng một mình - về cơ bản khôi phục một tập hợp các cột từ bản sao lưu của toàn bộ bảng. Nếu bạn muốn khôi phục tất cả các hàng, bỏ qua mệnh đề where.

Tất nhiên cách đơn giản hơn sẽ là xóa và chèn khi chọn, nhưng trong trường hợp của tôi, tôi cần một giải pháp chỉ với các bản cập nhật.

Mẹo nhỏ là khi bạn chọn * từ một cặp bảng có tên cột trùng lặp, bảng thứ 2 sẽ được đặt tên _1. Vì vậy, đây là những gì tôi đã đưa ra:

  update (
    select * from t1 join t2 on t2.id = t1.id
    where id in (
      select id from (
        select id, col1, col2, ... from t2
        minus select id, col1, col2, ... from t1
      )
    )
  ) set col1=col1_1, col2=col2_1, ...

Điều này không làm việc cho tôi trong Oracle 11g. Bạn có thể tạo một ví dụ làm việc của phương pháp này?
Jon Heller

-3
BEGIN
For i in (select id, name, desc from table2) 
LOOP
Update table1 set name = i.name, desc = i.desc where id = i.id and (name is null or desc is null);
END LOOP;
END;
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.