Trên Cập nhật khóa trùng lặp giống như chèn


158

Tôi đã tìm kiếm xung quanh nhưng không tìm thấy nếu có thể.

Tôi đã truy vấn MySQL này:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

Id trường có một "chỉ mục duy nhất", vì vậy không thể có hai trong số chúng. Bây giờ nếu cùng một id đã có trong cơ sở dữ liệu, tôi muốn cập nhật nó. Nhưng tôi có thực sự phải xác định lại tất cả các lĩnh vực này không, như:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

Hoặc là:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

Tôi đã chỉ định mọi thứ đã có trong phần chèn ...

Một lưu ý thêm, tôi muốn sử dụng công việc xung quanh để nhận ID!

id=LAST_INSERT_ID(id)

Tôi hy vọng ai đó có thể cho tôi biết cách hiệu quả nhất là gì.


9
AFAIK, phương pháp nghỉ ngơi mỗi cột, a=VALUES(a), b=VALUES(b), ...là cách bạn cần đi. Đó là cách tôi làm điều đó cho tất cả các INSERT ON DUPLICATE KEY UPDATEtuyên bố của mình .
Valdogg21

4
Chỉ cần làm rõ, tất cả mọi thứ được nêu ở đây là chính xác miễn là bạn hiểu câu hỏi đang được trả lời là: Bạn có phải trình bày lại mỗi cột bạn muốn CẬP NHẬT không? Có, nếu bạn muốn chèn hoặc cập nhật 8 cột trong bảng 25 cột, bạn phải nêu 8 cột hai lần - một lần trong phần chèn và một lần trong phần cập nhật. (Bạn có thể bỏ qua khóa chính trong phần cập nhật, nhưng dễ nhất là tạo trước một chuỗi "a = 1, b = 2, c = 3" và nhúng nó hai lần.) Tuy nhiên, bạn KHÔNG phải lặp lại 17 cột còn lại bạn không muốn thay đổi. Nếu nó trở thành CẬP NHẬT, họ sẽ giữ các giá trị hiện có của mình.
Timberline

3
Tôi đã thêm nhận xét đó bởi vì các câu hỏi và câu trả lời khiến tôi không chắc chắn về các cột không được đề cập với loại INSERT này, sau đó tôi đã kiểm tra sau khi tìm hiểu chính xác những gì cần kiểm tra. INSERT ON DUPLICATE KEY UPDATE giữ nguyên các cột không được đề cập trong UPDATE nhưng cung cấp cho chúng các giá trị mặc định trên INSERT. Với REPLACE INTO, các cột không được đề cập luôn nhận các giá trị mặc định, không bao giờ có giá trị.
Timberline

1
Kiểm tra stackoverflow.com/questions/2472229/ cấp , tôi nghĩ rằng có những gì bạn đang tìm kiếm
Chococroc

Câu trả lời:


82

Các UPDATEtuyên bố được đưa ra để các lĩnh vực lớn tuổi có thể được cập nhật để giá trị mới. Nếu các giá trị cũ của bạn giống với giá trị mới của bạn, tại sao bạn cần cập nhật nó trong mọi trường hợp?

Ví dụ. nếu cột của bạn ađể gđã được thiết lập như là 2để 8; sẽ không cần phải cập nhật lại.

Ngoài ra, bạn có thể sử dụng:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

Để có được idtừ LAST_INSERT_ID; bạn cần chỉ định ứng dụng phụ trợ mà bạn đang sử dụng cho cùng.

Đối với LuaSQL, conn:getlastautoid()tìm nạp giá trị.


6
Nó chỉ là ví dụ. Trong một truy vấn thực tế cuộc sống có sự khác biệt. Câu hỏi của tôi khá đơn giản: Tôi có thực sự cần chỉ định tất cả các trường (và giá trị trong ví dụ đầu tiên của tôi) nếu chúng giống như trong phần chèn không? Tôi chỉ muốn chèn tất cả hoặc nếu có một giá trị khớp duy nhất: cập nhật tất cả.
Roy

1
"Một lưu ý thêm, tôi cũng muốn sử dụng công việc xung quanh để lấy ID!"
Agamemnus

1
@Tresdin, Theo tôi có thể nói đó chỉ là cú pháp đường. Cá nhân tôi chưa bao giờ thấy ai sử dụng a = VALUES (a), b = VALUES (b), v.v. Có lẽ bạn có thể gán nhiều giá trị cho cột? Không chắc chắn lý do tại sao bạn sẽ không nối dữ liệu trước đó.
DuckPuncher

54
Nếu bảng tblđã có hàng (1,10) thì: INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)sẽ đặt a = 2 . While INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=asẽ đặt a = 10
Bùi Việt Thành

2
Tại sao tất cả các upvote? Điều này KHÔNG LÀM VIỆC. Như @ Bùi Việt Thành đã chỉ ra, sử dụng a=abản cập nhật không có gì. (Các truy vấn trong câu hỏi ban đầu cũng không thực sự cập nhật bất cứ điều gì, nhưng một bản cập nhật dường như là những gì OP dự định.)
Roger Dueck

41

Có một phần mở rộng cụ thể của MySQL đối với SQL có thể là những gì bạn muốn - REPLACE INTO

Tuy nhiên, nó không hoạt động hoàn toàn giống như 'CẬP NHẬT DU LỊCH'

  1. Nó xóa hàng cũ đụng độ với hàng mới và sau đó chèn hàng mới. Miễn là bạn không có khóa chính trên bàn thì sẽ ổn, nhưng nếu bạn làm thế, thì nếu có bảng nào khác tham chiếu khóa chính đó

  2. Bạn không thể tham chiếu các giá trị trong các hàng cũ để bạn không thể thực hiện tương đương với

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

Tôi muốn sử dụng công việc xung quanh để lấy ID!

Điều đó sẽ hoạt động - last_insert_id () sẽ có giá trị chính xác miễn là khóa chính của bạn tự động tăng.

Tuy nhiên, như tôi đã nói, nếu bạn thực sự sử dụng khóa chính đó trong các bảng khác, REPLACE INTOcó thể bạn sẽ không chấp nhận được, vì nó sẽ xóa hàng cũ bị xung đột thông qua khóa duy nhất.

Một số người khác đã đề xuất trước khi bạn có thể giảm một số thao tác gõ bằng cách thực hiện:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);

Vì vậy, tôi không thể gắn bó với khóa chính trước khi replace intotruy vấn?
Roy

Không - hàng đó bị xóa và một hàng mới được tạo, sẽ có một khóa chính mới.
Danack

Điều này có làm chậm quá trình trên các bộ dữ liệu lớn và như nhập csv với hàng 100K trở lên không?
Muhammad Omer Aslam

24

Không có cách nào khác, tôi phải chỉ định mọi thứ hai lần. Đầu tiên cho chèn, thứ hai trong trường hợp cập nhật.


9
Điều này là không chính xác và không nên là câu trả lời được chấp nhận. Câu trả lời từ @Danack là câu trả lời đúng.
Wayne

2
Bạn nên đọc lại câu hỏi @WayneWorkman, đây là câu trả lời đúng.
Roy

24

Đây là một giải pháp cho vấn đề của bạn:

Tôi đã cố gắng giải quyết vấn đề như của bạn và tôi muốn đề nghị kiểm tra từ khía cạnh đơn giản.

Thực hiện theo các bước sau: Học từ giải pháp đơn giản.

Bước 1: Tạo lược đồ bảng bằng Truy vấn SQL này:

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Bảng <code> người dùng </ code> có dữ liệu

Bước 2: Tạo một chỉ mục gồm hai cột để ngăn dữ liệu trùng lặp bằng Truy vấn SQL sau:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

hoặc, Tạo một chỉ mục của hai cột từ GUI như sau: Tạo chỉ mục từ GUI Chọn cột để tạo chỉ mục Các chỉ mục của bảng <code> người dùng </ code>

Bước 3: Cập nhật nếu tồn tại, chèn nếu không sử dụng các truy vấn sau:

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

Bảng <code> người dùng </ code> sau khi chạy truy vấn trên


1
Cảm ơn bạn đã hướng dẫn này - làm cho tôi hiểu, đánh giá cao, cảm ơn bạn!
Michael Nilsson

FYI lưu trữ mật khẩu trong văn bản thuần túy là một ý tưởng tồi tệ, vì đang cố gắng ngăn chặn mật khẩu trùng lặp ở cấp cơ sở dữ liệu.
OrangeDog

10

Chỉ trong trường hợp bạn có thể sử dụng ngôn ngữ kịch bản để chuẩn bị các truy vấn SQL của mình, bạn có thể sử dụng lại các cặp trường = value bằng cách sử dụng SETthay vì (a,b,c) VALUES(a,b,c).

Một ví dụ với PHP:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

Bảng ví dụ:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

4
Bạn nên thoát khỏi các giá trị của bạn hoặc thực hiện một tuyên bố đã chuẩn bị với các giá trị của bạn.
Chinoto Vokro

1
@ChinotoVokro - Tôi đồng ý. Đây chỉ là trình bày một "cách bạn có thể" và thậm chí không sử dụng bất kỳ hàm truy vấn nào trong ví dụ.
Chris

1
Đây là một thay thế tốt để chỉ định một mảng các khóa và một mảng các giá trị. Cảm ơn.
Mark Carpenter Jr

5

Bạn có thể muốn xem xét sử dụng REPLACE INTOcú pháp, nhưng được cảnh báo, khi trùng lặp khóa chính / UNIQUE, nó xóa dòng và chèn một hình mới.

Bạn sẽ không cần chỉ định lại tất cả các trường. Tuy nhiên, bạn nên xem xét việc giảm hiệu suất có thể (phụ thuộc vào thiết kế bảng của bạn).

Hãy cẩn thận:

  • Nếu bạn có khóa chính AUTO_INCREMENT, nó sẽ được cung cấp một khóa mới
  • Chỉ mục có thể sẽ cần phải được cập nhật

có một vi phạm ràng buộc nếu chỉ mục cũng là một khóa ngoại cho một bảng khác, nó được kích hoạt bằng cách xóa một phần.
dùng3290180

4

Tôi biết là muộn, nhưng tôi hy vọng ai đó sẽ được giúp đỡ về câu trả lời này

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

Bạn có thể đọc hướng dẫn dưới đây:

https://mariadb.com/kb/en/l Library / insert-on-d repeatate-key-update /

http://www.mysqltutorial.org/mysql-insert-or-update-on-d repeatate-key-update /


Câu trả lời tốt. Nhưng việc sử dụng VALUES () để chỉ các hàng mới không được chấp nhận kể từ MySQL 8.0.20 . Cách tiếp cận mới, chi tiết tại liên kết, là sử dụng bí danh cho hàng. Trong ví dụ này, bí danh là new:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473

@ user697473 Tôi đang gặp lỗi: "Bạn gặp lỗi trong cú pháp SQL của bạn và truy vấn của tôi đã hoạt động (trừ khi xảy ra khóa trùng lặp) trước khi tôi triển khai mệnh đề này. Có ý kiến ​​gì không?
aaron

@aaron - xin lỗi, không có ý tưởng tuyệt vời. MySQL 8.0.20 vừa được phát hành vào cuối tháng 4; có lẽ MariaDB chưa bắt kịp. Điều đó có thể giải thích tại sao nó gây ra lỗi cho bạn.
dùng697473

@ user697473 Vâng, tôi thực sự chỉ thử phương pháp a = VALUES (a) trong câu trả lời ban đầu và nó vẫn hoạt động, cảm ơn mọi người.
aaron

-7

bạn có thể sử dụng chèn bỏ qua cho trường hợp như vậy, nó sẽ bỏ qua nếu nó bị trùng lặp hồ sơ INSERT IGNORE ...; - không có KHÓA DUPLICATE


Tôi muốn chèn nó khi không có mặt, cập nhật nó. Không bỏ qua nó.
Roy

tại sao bạn muốn cập nhật nó nếu bạn đang cập nhật với giá trị trước đó / tương tự, nếu đó là một giá trị mới thì nó vẫn ổn với nó, nếu không chèn bỏ qua công việc cho nó
pitu

2
Rất có thể là do các giá trị có thể thay đổi và anh ta muốn tạo một bản ghi nếu không tồn tại, nhưng cập nhật một bản ghi hiện có nếu nó (có thay đổi) mà không có chi phí của chuyến đi khứ hồi trở lại ứng dụng và sau đó quay lại cơ sở dữ liệu lần nữa.
mopsyd 17/08/2015
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.