MySQL TRÊN KHÓA DUPLICATE - id chèn cuối cùng?


132

Tôi có câu hỏi sau:

INSERT INTO table (a) VALUES (0)
  ON DUPLICATE KEY UPDATE a=1

Tôi muốn ID của chèn hoặc cập nhật. Thông thường tôi chạy truy vấn thứ hai để có được điều này vì tôi tin rằng insert_id () chỉ trả về ID 'được chèn' chứ không phải ID được cập nhật.

Có cách nào để XÁC NHẬN / CẬP NHẬT và lấy ID của hàng mà không chạy hai truy vấn không?


3
Thay vì giả sử, tại sao bạn không tự kiểm tra? SQL trong bản chỉnh sửa ở trên không hoạt động và thông qua thử nghiệm của tôi nhanh hơn việc bắt lỗi chèn, sử dụng INSERT IGNORE hoặc chọn để xem có trùng lặp trước không.
Michael Fenwick

4
CẢNH BÁO: Giải pháp được đề xuất hoạt động, nhưng giá trị auto_increment tiếp tục tăng, ngay cả khi không có chèn. Nếu khóa trùng lặp xảy ra thường xuyên, bạn có thể muốn chạy alter table tablename AUTO_INCREMENT = 0;theo truy vấn trên, để tránh những lỗ hổng lớn trong giá trị id của bạn.
Frank Forte

Câu trả lời:


174

Kiểm tra trang này: https://web.archive.org/web/20150329004325/https://dev.mysql.com/doc/refman/5.0/en/insert-on-d repeatate.html
Ở cuối trang họ giải thích cách bạn có thể làm cho LAST_INSERT_ID có ý nghĩa đối với các bản cập nhật bằng cách chuyển một biểu thức cho hàm MySQL đó.

Từ ví dụ về tài liệu MySQL:

Nếu một bảng có chứa cột AUTO_INCREMENT và INSERT ... UPDATE chèn một hàng, hàm LAST_INSERT_ID () trả về giá trị AUTO_INCREMENT. Nếu câu lệnh cập nhật một hàng thay vào đó, LAST_INSERT_ID () không có ý nghĩa. Tuy nhiên, bạn có thể giải quyết vấn đề này bằng cách sử dụng LAST_INSERT_ID (expr). Giả sử id là cột AUTO_INCREMENT. Để làm cho LAST_INSERT_ID () có ý nghĩa đối với các bản cập nhật, hãy chèn các hàng như sau:

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;

2
Bằng cách nào đó tôi đã bỏ lỡ điều đó khi nhìn vào trang đó. Vì vậy, phần cập nhật xuất hiện dưới dạng: UPDATE id = LAST_INSERT_ID (id) Và nó hoạt động rất tốt. Cảm ơn!
thekevinscott

7
Người ta nói rằng hàm php mysql_insert_id () trả về giá trị đúng trong cả hai trường hợp: php.net/manual/en/feft.mysql-insert-id.php#59718 .
jayarjo

2
@PetrPeller - tốt, không cần nhìn vào phần bên trong của MySQL, điều đó có thể có nghĩa là nó sẽ tạo ra một giá trị, nhưng giá trị đó không liên quan đến truy vấn bạn vừa chạy. Nói cách khác, một vấn đề là một nỗi đau để gỡ lỗi.
Jason

13
Sau 5.1.12, điều này được cho là không còn cần thiết, tuy nhiên tôi đã tìm thấy một ngoại lệ cho ngày hôm nay. Nếu bạn có pk tự động và một khóa duy nhất để nói địa chỉ email và 'kích hoạt cập nhật trùng lặp' dựa trên địa chỉ email, lưu ý rằng last_insert_id 'sẽ KHÔNG phải là giá trị tự động của hàng được cập nhật. Nó dường như là giá trị tự động được chèn gần đây nhất. Cái này tạo ra một sự khác biệt lớn. Các công việc xung quanh giống như được hiển thị ở đây, cụ thể là sử dụng id = LAST_INSERT_ID (id) trong truy vấn cập nhật.
sckd

1
Trên bình luận của 5.5 @ sckd vẫn đúng.
e18r

37

Chính xác, nếu đây là truy vấn ban đầu:

INSERT INTO table (a) VALUES (0)
 ON DUPLICATE KEY UPDATE a=1

và 'id' là khóa chính tăng tự động hơn đây sẽ là giải pháp hoạt động:

INSERT INTO table (a) VALUES (0)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), a=1

Có phải tất cả ở đây: http://dev.mysql.com/doc/refman/5.0/en/insert-on-d repeatate.html

Nếu một bảng có chứa cột AUTO_INCREMENT và INSERT ... UPDATE chèn một hàng, hàm LAST_INSERT_ID () trả về giá trị AUTO_INCREMENT. Nếu câu lệnh cập nhật một hàng thay vào đó, LAST_INSERT_ID () không có ý nghĩa. Tuy nhiên, bạn có thể giải quyết vấn đề này bằng cách sử dụng LAST_INSERT_ID (expr). Giả sử id là cột AUTO_INCREMENT.


7
Vâng, xem câu trả lời được chấp nhận cho cùng những gì bạn nói. Không cần phải hồi sinh bài viết 3 năm tuổi. Cảm ơn cho nỗ lực của bạn nào.
FancyPants

1
@tombom lý do duy nhất tại sao tôi đăng câu trả lời này là vì câu trả lời được chấp nhận là không chính xác - nó sẽ không hoạt động nếu không có gì để cập nhật.
Aleksandar Popovic

2

Bạn có thể xem REPLACE, về cơ bản là xóa / chèn nếu bản ghi tồn tại. Nhưng điều này sẽ thay đổi trường tăng tự động nếu có, có thể phá vỡ mối quan hệ với dữ liệu khác.


1
À đúng rồi - Tôi đang tìm kiếm thứ gì đó sẽ không thoát khỏi ID trước đó
thekevinscott

Điều này cũng có thể nguy hiểm vì điều này cũng có thể gây ra việc xóa một dữ liệu liên quan khác (bởi các ràng buộc).
Serge


1

Tôi đã gặp một vấn đề, khi ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID (id) tăng khóa chính lên 1. Vì vậy, id của đầu vào tiếp theo trong phiên sẽ được tăng thêm 2


0

Điều đáng chú ý và điều này có thể rõ ràng (nhưng dù sao tôi cũng sẽ nói rõ ràng ở đây), REPLACE sẽ thổi bay hàng phù hợp hiện có trước khi chèn dữ liệu mới của bạn. TRÊN CẬP NHẬT KHÓA NGHIÊM TRỌNG sẽ chỉ cập nhật các cột bạn chỉ định và giữ nguyên hàng.

Từ hướng dẫn :

REPLACE hoạt động chính xác như INSERT, ngoại trừ nếu một hàng cũ trong bảng có cùng giá trị với một hàng mới cho KEY PRIMARY hoặc chỉ mục UNIQUE, hàng cũ sẽ bị xóa trước khi hàng mới được chèn.


0

Các giải pháp hiện có hoạt động nếu bạn sử dụng tự động. Tôi có một tình huống mà người dùng có thể xác định một tiền tố và nó sẽ khởi động lại chuỗi ở 3000. Do tiền tố khác nhau này, tôi không thể sử dụng tính năng tự động, điều này làm cho last_insert_id trống để chèn. Tôi đã giải quyết nó bằng cách sau:

INSERT INTO seq_table (prefix, id) VALUES ('$user_prefix', LAST_INSERT_ID(3000)) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id + 1);
SELECT LAST_INSERT_ID();

Nếu tiền tố tồn tại, nó sẽ tăng nó và điền vào last_insert_id. Nếu tiền tố không tồn tại, nó sẽ chèn tiền tố với giá trị 3000 và điền vào last_insert_id với 3000.

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.