запит щодо поєднання оновлення та вставлення запиту в один запит у mysql


9

Я хочу відстежувати історію змін для користувача, так що щоразу, коли він змінює свій профіль, мені потрібно взяти старі дані і зберігати в історії та оновлювати нові дані.

Я можу використовувати A, selectщоб отримати старі дані, insertісторію та, нарешті, updateзмінити дані.

чи можу я мати все це в одному запиті в mysql без використання збережених процедур, тригерів і т. д., як використання замків тощо. Якщо так, дайте мені невеликий зразок.


1
@savaranan: Це питання вартує +1, оскільки він настійно нагадує DBA та розробникам використовувати транзакції та в повній мірі використовувати властивості ACID бази даних.
RolandoMySQLDBA

2
@savaranan: Для всіх намірів і цілей Джек надав ТОЛЬКО правдоподібну відповідь. Насправді, Джек Дуглас зробив це додатковим кроком і змусив переривчасте блокування в кожному рядку з id = 10 для додаткового захисту MVCC, зробивши SELECT ... ДЛЯ ОНОВЛЕННЯ. Його відповідь ще більше наголошує на питанні Джека, і я весь час говорив: UPDATE та INSERT не можуть бути, і ніколи не бути одним запитом, вони можуть бути лише однією транзакцією для поведінки SQL, яку пропонує ваше питання.
RolandoMySQLDBA

Відповіді:


13

Для того, щоб зробити це без ризику блокування іншого користувача намагається оновити той же профіль , в той же час, необхідно зафіксувати рядок в t1першому, а потім використовувати транзакцію (як Роландо вказує в коментарях до вашого запитання):

start transaction;
select id from t1 where id=10 for update;
insert into t2 select * from t1 where id=10;
update t1 set id = 11 where id=10;
commit;

Це просто геніально для подальшого блокування кожного рядка з id = 10. Це має бути +2. Все, що я можу дати - це +1 !!!
RolandoMySQLDBA

1

Я не вірю, що існує спосіб поєднати всі три твердження. Найближче до цього насправді не допомагає вам, і це ВИБІР НАСТРОЮ. Ваша найкраща ставка - курок. Нижче наведено зразок тригера, який я часто використовую, щоб підтримувати саме такий аудиторський слід (побудований за допомогою PHP):

$trigger = "-- audit trigger --\nDELIMITER $ \n".
    "DROP TRIGGER IF EXISTS `{$prefix}_Audit_Trigger`$\n".
    "CREATE TRIGGER `{$prefix}_Audit_Trigger` AFTER UPDATE ON `$this->_table_name` FOR EACH ROW BEGIN\n";

foreach ($field_defs as $field_name => $field) {
    if ($field_name != $id_name) {
       $trigger .= "IF (NOT OLD.$field_name <=> NEW.$field_name) THEN \n".'INSERT INTO AUDIT_LOG ('.
                    'Table_Name, Row_ID, Field_Name, Old_Value, New_Value, modified_by, DB_User) VALUES'.
                    "\n ('$this->_table_name',OLD.$this->_id_name,'$field_name',OLD.$field_name,NEW.$field_name,".
                    "NEW.modified_by, USER()); END IF;\n";
    }
}
$trigger .= 'END$'."\n".'DELIMITER ;';

-3

Я виявив, що цей запит працює на серверах SQL та MySQL INSERT INTO t2 SELECT * FROM t1 WHERE id=10; UPDATE t1 SET id=11 WHERE id=10;

Сподіваюся, що це стане в нагоді і іншим, і в майбутньому.


4
Це насправді не запит. Це фактично два запити, які слід трактувати як транзакцію.
RolandoMySQLDBA

@rolandomysqldba: це прекрасно працює як один запит, коли я надсилаю на db-сервер з коду програми, де я розглядаю цей набір як єдиний запит. чому ти так кажеш?. чи можете ви спростувати це з вагомих причин ..
Сараванан

2
@saravanan: В очах InnoDB або будь-яких сумісних з ACID RDBMS (Oracle, SQLServer, PostreSQL, Sybase тощо) неможливо назвати ці два оператори SQL одним запитом. Оскільки база даних, сумісних з ACID, розглядає їх як два твердження. За замовчуванням InnoDB увімкнено автокомісію. Перше твердження INSERT виконуватиметься як одна операція. Дані мультиверсійного контролю кон'юнктури (MVCC) будуть створені для збереження копії вихідних даних у таблиці t2 послідовно. Якщо MySQL виходить з ладу під час виконання INSERT, InnoDB використовує дані MVCC для відкату t2 до початкового стану.
RolandoMySQLDBA

1
@saravanan: Припустимо, INSERT успішно працював. Дані, отримані в результаті INSERT, були скоєні (при ввімкненій автоматичній передачі даних), а таблиця захисту MVCC t2 відкидається. Коли ви виконуєте UPDATE, MVCC генерується проти таблиці t1 і виконується UPDATE. Якщо MySQL виходить з ладу під час оновлення, InnoDB використовує дані MVCC на t1 для відкату UPDATE. Навіть якщо ОНОВЛЕННЯ змінює лише один рядок, існує одна-на-мільйонна можливість переміщення записів з t1 в t2 з id 10 і не змінити id 10 на id 11 в t1. Щоб запобігти цьому унікальному сценарію, вам потрібно зробити наступне ...
RolandoMySQLDBA

@savaranan: трактуйте два оператори SQL як одну транзакцію. Простий спосіб зробити це: ПОЧАТОК; ВСТАВИТИ В t2 SELECT * ІЗ t1 WHERE id = 10; ОНОВЛЕННЯ t1 SET id = 11 WHERE id = 10; КОМІТЕТ; Найсильнішою причиною трактування двох операторів SQL як єдиної транзакції є той факт, що MVCC, створений для INSERT, залишився б існуванням під час оновлення. Якщо збій MySQL повинен відбутися під час оновлення всередині транзакції (BEGIN; ... COMMIT; блок), MVCC відкине всі зміни до послідовного стану. Якщо і INSERT, і UPDATE завершено, MVCC відкидається в останній момент.
RolandoMySQLDBA
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.