Chèn vào bảng MySQL hoặc cập nhật nếu có


875

Tôi muốn thêm một hàng vào bảng cơ sở dữ liệu, nhưng nếu một hàng tồn tại với cùng một khóa duy nhất tôi muốn cập nhật hàng.

Ví dụ:

insert into table (id, name, age) values(1, "A", 19)

Giả sử khóa duy nhất là id, và trong Cơ sở dữ liệu của tôi , có một hàng với id = 1. Trong trường hợp đó, tôi muốn cập nhật hàng đó với các giá trị này. Thông thường điều này cho một lỗi.
Nếu tôi sử dụng insert IGNOREnó sẽ bỏ qua lỗi, nhưng nó vẫn không cập nhật.


5
SQL cần một cú pháp chính thức cho trường hợp sử dụng này mà không bắt buộc sao chép các giá trị trong cú pháp và giữ nguyên khóa chính.
Pete Alvin

Để có được id ảnh hưởng, hãy tham khảo MySQL TRÊN KHÓA DUPLICATE - id chèn cuối cùng?
Kris Roofe

Hãy cẩn thận: kể từ phiên bản 5.7, cách tiếp cận này không hỗ trợ trực tiếp mệnh đề WHERE như một phần của hoạt động INSERT / UPDATE. Ngoài ra, một CẬP NHẬT thực sự được tính là hai hoạt động riêng biệt (XÓA và XÁC NHẬN) ... trong trường hợp quan trọng cho mục đích kiểm toán. (Tìm hiểu)
dreftymac

Câu trả lời:


1599

Sử dụng INSERT ... ON DUPLICATE KEY UPDATE

TRUY VẤN:

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name="A", age=19

86
+1 Từ những gì tôi đã tìm thấy, phương pháp này ít gặp vấn đề hơn đối với các phím tăng tự động và các va chạm khóa duy nhất khác so với REPLACE INTO, và nó hiệu quả hơn.
Andrew Oblley

46
Tôi biết tất cả các bạn ám chỉ điều này, nhưng tôi muốn rõ ràng cho người khác. Nếu ID bạn chèn KHÔNG phải là CHÍNH CHÍNH hoặc ĐỘC ĐÁO, thì điều này sẽ không hoạt động. Điều này ban đầu không hiệu quả với tôi vì ID của tôi không phải là duy nhất.
Keven

7
Tôi tự hỏi tại sao affected row countkết quả 2khi cập nhật thành công (trên khóa trùng lặp) một hàng? Bất cứ ai khác có kết quả này? (Dữ liệu được cập nhật chính xác: có nghĩa là chỉ có 1 hàng được cập nhật)
Dimitry K

15
Điều này hơi muộn, nhưng dù sao đi nữa: nó được nêu trong hướng dẫn cập nhật ON DUPLICATE KEY UPDATEtăng các hàng bị ảnh hưởng lên 2. Nó báo cáo 0 nếu không có gì thực sự được cập nhật (giống như thông thường UPDATE).
Vatev

61
Cũng lưu ý rằng bạn có thể sử dụng GIÁ TRỊ (tên) để tham chiếu đến giá trị bạn cố gắng chèn, ví dụ: bảng INSERT INTO (id, tên, tuổi) GIÁ TRỊ (1, "A", 19) TRÊN DUPLICATE KEY UPDATE name = VALUES (tên) , tuổi = GIÁ TRỊ (tuổi)
Tò mò Sam

246

Kiểm tra thay thế

http://dev.mysql.com/doc/refman/5.0/en/replace.html

REPLACE into table (id, name, age) values(1, "A", 19)

11
@Piontek Bởi vì cái này ngắn hơn và dễ hiểu hơn và không ai giải thích được tại sao "chèn vào bản sao" lại tốt hơn.
Mr_Chimp

77
nó thay đổi ID của hồ sơ và do đó có thể phá hủy các tài liệu tham khảo nước ngoài.
boh

102
Vấn đề khác với REPLACE INTO là bạn phải chỉ định các giá trị cho TẤT CẢ các trường ... nếu không các trường sẽ bị mất hoặc được thay thế bằng các giá trị mặc định. REPLACE INTO về cơ bản xóa hàng nếu nó tồn tại và chèn hàng mới. Trong ví dụ này, nếu bạn đã thực hiện 'REPLACE INTO bảng (id, age) giá trị (1, 19) thì trường tên sẽ trở thành null.
Dale

33
Đây thực sự là XÓA toàn bộ hàng và thực hiện INSERT mới.
mjb

8
@mjb Do đó, bạn cần phải có các đặc quyền XÓA, tốt nhất là không nên có nếu bạn không cần chúng. Người dùng cơ sở dữ liệu của môi trường của tôi chỉ có quyền INSERT và UPDATE, vì vậy REPLACE sẽ không hoạt động.
ndm13

54

Khi sử dụng hàng loạt chèn sử dụng cú pháp sau:

INSERT INTO TABLE (id, name, age) VALUES (1, "A", 19), (2, "B", 17), (3, "C", 22)
ON DUPLICATE KEY UPDATE
    name = VALUES (name),
    ...

Rất hữu ích nếu bạn muốn thao túng giá trị mới
Hunger

Nếu VALUES(name)không hiệu quả, bạn có thể thửname = 'name_value',...;
Son Phạm

26

Thử thứ này đi:

INSERT INTO table (id, name, age) VALUES (1, 'A', 19) ON DUPLICATE KEY UPDATE id = id + 1;

Hi vọng điêu nay co ich.


6
thực sự tôi không cần thêm các giá trị mới vào một hàng khác bằng ID mới thay vào đó tôi muốn thay thế các giá trị hiện tại của id = 1 bằng các giá trị này. (vì tôi hiểu điều này làm tăng id và thêm dữ liệu)
Keshan

49
Tôi không nghĩ anh ấy muốn tăng id lên từng bản sao.
Donnie

7
"vi phạm" không phải là từ bạn đang tìm kiếm :) Thật không may, bây giờ tôi đã thấy "vi phạm", tôi không còn có thể hình dung được từ thực tế ..
mwfearnley

1
Bạn đang tìm kiếm "traverses" hoặc "cover"?
Chris Dev

4
@mwfearnley Từ bạn đang cố gắng hình dung là "siêu việt". Chỉ mất một năm rưỡi :-)
Michael Krebs

20

Thử cái này:

INSERT INTO table (id,name,age) VALUES('1','Mohammad','21') ON DUPLICATE KEY UPDATE name='Mohammad',age='21'

Lưu ý:
Ở đây nếu id là khóa chính thì sau lần chèn đầu tiên với id='1'mỗi lần cố gắng chèn id='1'sẽ cập nhật tên và tuổi và tuổi tên trước đó sẽ thay đổi.


Tôi muốn làm việc mà không sử dụng id . Bạn đã thử mà không có khóa chính?
vào

@ersks xem câu hỏi. người dùng hỏi về khi nào có một khóa duy nhất
Rasel

Tôi hiểu điều đó, nhưng tôi đang cố gắng giải quyết vấn đề của mình, trong đó những giá trị này chỉ được biết. Vì vậy, trong tình huống như vậy tôi đã viết bình luận trên hy vọng giải pháp chính xác.
vào

15
INSERT IGNORE INTO table (id, name, age) VALUES (1, "A", 19);

INSERT INTO TABLE (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE NAME = "A", AGE = 19;

REPLACE INTO table (id, name, age) VALUES(1, "A", 19);

Tất cả những giải pháp này sẽ làm việc liên quan đến câu hỏi của bạn.

Nếu bạn muốn biết chi tiết về những tuyên bố này, hãy truy cập liên kết này


REPLACEkhông được ghi lại trong tài liệu được liên kết.
Ray Baxter

truy vấn sql đầy lỗi chính tả, ví dụ của bạn sẽ không hoạt động. Đó là CHERTN VÀO BẢNG (không phải VÀO BẢNG) và CẬP NHẬT KHÓA CẬP NHẬT (không phải trên CẬP NHẬT CẬP NHẬT). Không chắc ai đã ủng hộ điều này
Robert Sinclair

1
Xin lỗi vì lỗi chính tả. Cảm ơn đã sửa lỗi cho tôi
Dilraj Singh

11

Khi sử dụng SQLite:

REPLACE into table (id, name, age) values(1, "A", 19)

Với điều kiện idlà khóa chính. Hoặc nếu không nó chỉ chèn một hàng khác. Xem CHERTN (SQLite).


Cái gì replace intochính xác là "chèn vào, hoặc cập nhật khi có". @Owl
DawnSong

+1 (1 trên 3) Tôi thấy điều này hoạt động cho tình huống của mình - Tôi cần thay thế một hàng hiện có bằng một khóa duy nhất hoặc nếu không có thì hãy thêm hàng đó. Đơn giản nhất của các giải pháp ở đây.
trị liệu

(2 trên 3) Truy vấn của tôi:CREATE TRIGGER worklog_update AFTER INSERT ON worklog FOR EACH ROW REPLACE INTO support_time_monthly_hours ( ProjectID, monthTotalTime, Year, Month ) SELECT jiraissue.PROJECT, SUM(worklog.timeworked), YEAR(CURRENT_DATE()), MONTH(CURRENT_DATE()) FROM worklog, jiraissue WHERE worklog.issueid = jiraissue.ID AND jiraissue.PROJECT = (SELECT PROJECT FROM jiraissue WHERE NEW.issueid = jiraissue.ID ) AND worklog.startdate BETWEEN DATE_FORMAT(NOW() ,'%Y-%m-01 00:00:00') AND NOW();
trị liệu

(3 trên 3) Câu hỏi / câu trả lời liên quan stackoverflow.com/questions/41767517/ trên
trị liệu

2
Vấn đề với điều này trong mysql là việc thay thế sẽ loại bỏ các giá trị khác trong trường hợp chúng không được cung cấp. Tuy nhiên, giải pháp @ fabiano-souza thích hợp hơn
Quamber Ali

11

Chỉ vì tôi ở đây để tìm giải pháp này nhưng để cập nhật từ một bảng có cấu trúc giống hệt nhau (trong trường hợp trang web của tôi kiểm tra DB để DB trực tiếp):

INSERT  live-db.table1
SELECT  *
FROM    test-db.table1 t
ON DUPLICATE KEY UPDATE
        ColToUpdate1 = t.ColToUpdate1,
        ColToUpdate2 = t.ColToUpdate2,
        ...

Như đã đề cập ở nơi khác, chỉ có các cột bạn muốn cập nhật cần được đưa vào sau ON DUPLICATE KEY UPDATE.

Không cần phải liệt kê các cột trong INSERThoặc SELECT, mặc dù tôi đồng ý rằng nó có thể thực hành tốt hơn.


4

Trong trường hợp bạn muốn tạo một non-primarytrường làm tiêu chí / điều kiện cho ON DUPLICATE, bạn có thể tạo một UNIQUE INDEXkhóa trên bảng đó để kích hoạt DUPLICATE.

ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`);

Và trong trường hợp bạn muốn kết hợp hai trường để làm cho nó trở nên độc nhất trên bảng, bạn có thể đạt được điều này bằng cách thêm nhiều hơn vào tham số cuối cùng.

ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`, `age`);

Lưu ý, chỉ cần đảm bảo xóa đầu tiên tất cả dữ liệu có cùng giá trị nameagegiá trị trên các hàng khác.

DELETE table FROM table AS a, table AS b WHERE a.id < b.id 
AND a.name <=> b.name AND a.age <=> b.age;

Sau đó, nó sẽ kích hoạt ON DUPLICATEsự kiện.

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name = VALUES(name), age = VALUES(age)

2

Trong trường hợp của tôi, tôi đã tạo các truy vấn bên dưới nhưng trong truy vấn đầu tiên nếu id1 đã tồn tại và tuổi đã ở đó, sau đó nếu bạn tạo truy vấn đầu tiên mà không có agegiá trị agesẽ không có

REPLACE into table SET `id` = 1, `name` = 'A', `age` = 19

để tránh vấn đề trên, hãy tạo truy vấn như bên dưới

INSERT INTO table SET `id` = '1', `name` = 'A', `age` = 19 ON DUPLICATE KEY UPDATE `id` = "1", `name` = "A",`age` = 19

có thể nó sẽ giúp bạn ...


2
Có ai biết tại sao chúng ta phải gán các giá trị hai lần? Tại sao MySQL không cho phép chúng tôi kết thúc truy vấn tại ON DUPLICATE KEY UPDATE mà không sao chép tất cả các câu lệnh gán? Một số bảng cơ sở dữ liệu có nhiều cột và điều này có vẻ dư thừa / vô cớ. Tôi hiểu tại sao chúng ta có tùy chọn cho các bài tập thay thế, nhưng tại sao không có tùy chọn để bỏ qua chúng? Chỉ tò mò nếu có ai biết.
Nước xanh

2

Trong trường hợp, bạn muốn giữ trường cũ (Ví dụ: tên). Truy vấn sẽ là:

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name=name, age=19;
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.