Diuji pada MySQL 5.5.14.
CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
DELIMITER //
CREATE PROCEDURE merge_db(k INT, data TEXT)
BEGIN
DECLARE done BOOLEAN;
REPEAT
BEGIN
-- If there is a unique key constraint error then
-- someone made a concurrent insert. Reset the sentinel
-- and try again.
DECLARE ER_DUP_UNIQUE CONDITION FOR 23000;
DECLARE CONTINUE HANDLER FOR ER_DUP_UNIQUE BEGIN
SET done = FALSE;
END;
SET done = TRUE;
SELECT COUNT(*) INTO @count FROM db WHERE a = k;
-- Race condition here. If a concurrent INSERT is made after
-- the SELECT but before the INSERT below we'll get a duplicate
-- key error. But the handler above will take care of that.
IF @count > 0 THEN
UPDATE db SET b = data WHERE a = k;
ELSE
INSERT INTO db (a, b) VALUES (k, data);
END IF;
END;
UNTIL done END REPEAT;
END//
DELIMITER ;
CALL merge_db(1, 'david');
CALL merge_db(1, 'dennis');
Beberapa pemikiran:
- Anda tidak dapat melakukan pembaruan terlebih dahulu lalu periksa
@ROW_COUNT()
karena mengembalikan jumlah baris yang benar-benar berubah. Ini bisa menjadi 0 jika baris sudah memiliki nilai yang Anda coba perbarui. - Juga,
@ROW_COUNT()
tidak aman untuk direplikasi. - Anda dapat menggunakan
REPLACE...INTO
. - Jika menggunakan InnoDB atau tabel dengan dukungan transaksi, Anda mungkin dapat menggunakan
SELECT...FOR UPDATE
(belum diuji).
Saya tidak melihat keuntungan dari solusi ini daripada hanya menggunakan INSERT...ON DUPLICATE KEY UPDATE
.