Які практичні відмінності між "ЗАМІНИТИ" та "ВСТАВИТИ ... НА ДОВОЛНЕННЯ КЛЮЧОВОГО ОНОВЛЕННЯ" в MySQL?


81

Мені потрібно встановити значення всіх полів запису певним ключем (ключ фактично складений), вставляючи запис, якщо запису з таким ключем ще немає.

REPLACE здається, це призначено для виконання роботи, але в той же час його сторінка керівництва пропонує INSERT ... ON DUPLICATE KEY UPDATE .

Якого з них мені краще вибрати і чому?

Єдиний "побічний ефект", REPLACEякий мені спадає на думку, полягає в тому, що він збільшує значення автоінкременту (на щастя, я не використовую жодного), хоча, INSERT ... ON DUPLICATE KEY UPDATEмабуть, не буде. Які інші практичні відмінності слід мати на увазі? У яких конкретних випадках можна REPLACEвіддати перевагу INSERT ... ON DUPLICATE KEY UPDATEта навпаки?


INSERT ... ON DUPLICATE KEY UPDATE фактично також збільшує лічильник автоінкременту. Не для запису, який оновлюється, а для наступного вставленого запису. Так що, якщо високий ідентифікатор 10 і ви робите дублікат вставки, а потім вставляє нове унікальне значення, що ID рядки »s буде 12.
marlar

Відповіді:


117

REPLACEвнутрішньо виконує видалення, а потім вставку. Це може спричинити проблеми, якщо обмеження зовнішнього ключа вказує на цей рядок. У цій ситуації REPLACEможе відбутися збій або ще гірше: якщо для вашого зовнішнього ключа встановлено каскадне видалення, REPLACEрядки з інших таблиць будуть видалені. Це може статися, навіть якщо обмеження було задоволено як до, так і після REPLACEоперації.

Використання INSERT ... ON DUPLICATE KEY UPDATEдозволяє уникнути цієї проблеми і тому є кращим.


1
Хороша відповідь, але у фактичному моєму випадку ця проблема не буде вирішена. Хоча шанс зіткнення можна вважати 50/50. Що мені тоді вибрати? І як INSERT ... ON DUPLICATE KEY UPDATEвиглядає значно "краще", то в яких конкретних випадках "ЗАМІНА" може бути кращим вибором?
Іван

3
Я провів досить значне дослідження, і, наскільки я можу зрозуміти, немає жодної загальної причини використовувати ЗАМІНИ замість ВСТАВИТИ ... НА ДВОЙНОМУ КЛЮЧОВОМУ ОНОВЛЕННІ. По суті, це застаріла функція. Якщо немає якоїсь конкретної причини, чому ваш код покладається на видалення та повторне додавання рядків із пов’язаними ефектами на індекси та значенням автоматичного збільшення, не видається жодної причини використовувати його.
Nathan Stretch,

2
Увімкне REPLACEоновить значення PK з автоматичним збільшенням, якщо це робить а DELETEта INSERT. Що саме те, що я хочу. Я не хочу, щоб споживач знаходив запис під тим самим ПК, тому вони не отримують рядків. Коли я хочу, щоб вони його знайшли (фактичне оновлення), я використовуюUPDATE
radtek

Таким чином, інша половина питання: коли ви віддаєте перевагу REPLACEбільш INSERT ... ON DUPLICATE KEY UPDATE? Чому INSERT+ DELETEколи-небудь було б кращим над знаком UPDATE?
LemonPi,

59

Щоб відповісти на запитання з точки зору продуктивності, я зробив тест, використовуючи обидва методи

Замінити на включає: 1.
Спробуйте вставити в таблицю
2. Якщо 1 не вдається, видаліть рядок і вставте новий рядок

Вставити на дублікат Оновлення ключа передбачає: 1.
Спробуйте вставити в таблицю
2. Якщо 1 не вдається, оновіть рядок

Якщо виконано всі кроки вставки, не повинно бути різниці у продуктивності. Швидкість повинна залежати від кількості задіяних оновлень. Найгірший випадок - це коли всі твердження є оновленнями

Я спробував обидва твердження в моїй таблиці InnoDB, що включають 62 510 записів (лише оновлення). На швидкості кемпінгу:
Замінити на: 77,411 секунд
Вставити на дублікат оновлення ключа: 2,446 секунд

Insert on Duplicate Key update is almost 32 times faster.

Розмір таблиці: 1 249 250 рядків з 12 стовпцями на Amazon m3.medium


Класна статистика, ти пробував Insert on Duplicate Key Replace? Це було повільніше?
radtek

@radtek ти можеш лише писати ON DUPLICATE KEY UPDATE, ти не можеш писати ON DUPLICATE KEY REPLACE. Якщо ви хочете оновити всі значення існуючого рядка за дублікатом ключа, вам потрібно написати ON DUPLICATE KEY UPDATE col1=VALUES(col1), col2=VALUES(col2), ...- вам доведеться перерахувати всі стовпці вручну.
izogfif

Я знаю, що я просто запитував, що було швидше, і схоже на оновлення.
radtek

9

При використанні REPLACEзамість INSERT ... ON DUPLICATE KEY UPDATE, я іноді спостерігаю проблеми із блокуванням або блокуванням ключів, коли кілька запитів швидко надходять для даного ключа. Атомність останнього (крім того, що не спричиняє каскадного видалення) є тим більше підставою для його використання.


3

Якщо ви не перерахуєте всі стовпці, я думаю REPLACE, скине всі невказані стовпці зі значеннями за замовчуванням у замінених рядках. ON DUPLICATE KEY UPDATEзалишатиме незмінені стовпці без змін.


3

У яких конкретних випадках можна замінити ЗАМІНУ, а НЕ ВСТАВИТИ ... НА ДОВОЛНЕННЯ КЛЮЧОВОГО ОНОВЛЕННЯ і навпаки?

Я щойно зрозумів, що у випадку таблиць із ФЕДЕРАТОВАНИМ механізмом зберігання INSERT...ON DUPLICATE KEY UPDATEоператори приймаються, але не вдаються (з помилкою 1022: Не вдається записати; дублікат ключа в таблиці ...), якщо дублікат ключа відбувається порушення - див. відповідний пункт на цій сторінці довідкового посібника MySQL.

На щастя, я зміг використати REPLACEзамість INSERT...ON DUPLICATE KEY UPDATEмого тригера після вставки, щоб досягти бажаного результату реплікації змін до ФЕДЕРАТИРОВАНОЇ таблиці.


2

Replace, здається, робить дві операції у випадку, якщо ключ уже існує. Можливо, це означає, що між ними є різниця в швидкості?

(ВСТАВИТИ) одне оновлення проти одне видалення + одна вставка (ЗАМІНИ)

РЕДАКТУВАТИ: Мої натяки на те, що заміна може бути повільнішою, насправді абсолютно неправильні. Ну, згідно з цим повідомленням у блозі все одно ... http://www.tokutek.com/2010/07/why-insert-on-duplicate-key-update-may-be-slow-by-incurring-disk-seeks /



0

ЗМІНИТИ іноді потрібно, оскільки INSERT IGNORE, здається, не працює з перетвореннями даних.

Якщо я це роблю, я встановлюю лише найбільшийCityPop:

ВСТАВІТЬ ІГНОРУВАТИ У найбільші міста (stateID, greatestCityPop, statePop) ВИБЕРІТЬ stateID, MAX (city.pop) як найбільшийCityPop, state.pop ІЗ міста ПРИЄДНАЙТЕСЬ до держави city.stateID = state.ID GROUP BY city.stateID ON DUPLICATE KEY UPDATE greatestCityPop = greatestCityPop

Якщо я це роблю, я неправильно використовую функцію GROUP:

ВСТАВІТЬ ІГНОРУВАТИ У найбільші міста (stateID, greatestCityPop, statePop) ВИБЕРІТЬ stateID, MAX (city.pop) як найбільшийCityPop, state.pop ІЗ міста ПРИЄДНАЙТЕСЬ стан у місто.stateID = state.ID GROUP BY city.stateID ON DUPLICATE KEY UPDATE greatestCityPop = MAX (city.pop)

І якщо я це зроблю, MySQL не розпізнає назву стовпця:

ВСТАВІТЬ ІГНОРУВАТИ У найбільші міста (stateID, greatestCityPop, statePop) ВИБЕРІТЬ stateID, MAX (city.pop) як найбільшийCityPop, state.pop ІЗ міста ПРИЄДНАЙТЕСЬ стан у місто.stateID = state.ID GROUP BY city.stateID ON DUPLICATE KEY UPDATE mostCityPop = city .largestCityPop

Це працює, але здається просто потворним:

ВСТАВІТЬ ІГНОРУВАТИ У найбільші міста (stateID, greatestCityPop, statePop) SELECT * FROM (SELECT stateID, MAX (city.pop) як greatestCityPop, state.pop FROM city JOIN state on city.stateID = state.ID GROUP BY city.stateID) x ON ДУБЛІКУВАТИ КЛЮЧОВЕ ОНОВЛЕННЯ найбільшеCityPop = найбільшеCityPop


Обережно: INSERT IGNOREзапит завершиться успішно (і видасть попередження), якщо зовнішнє обмеження не вдасться ! Якщо ви хочете виявити таку помилку, краще використовуйте ON DUPLICATE KEY UPDATEбез IGNORE.
izogfif
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.