запрос относительно объединения обновления и запроса вставки в один запрос в MySQL


9

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

Я могу использовать selectдля получения старых данных, insertдля истории и, наконец, updateдля изменения данных.

Могу ли я иметь все это в одном запросе в MySQL без использования хранимых процедур, триггеров и т. д., как с помощью блокировок и т. д., если да, дайте мне небольшой пример.


1
@savaranan: Этот вопрос заслуживает +1, потому что он настоятельно напоминает администраторам баз данных и разработчикам использовать транзакции и в полной мере использовать свойства ACID базы данных.
RolandoMySQLDBA

2
@savaranan: Для всех намерений и целей Джек предоставил ЕДИНСТВЕННЫЙ правдоподобный ответ, который есть. Фактически, Джек Дуглас предпринял дополнительный шаг и принудительно заблокировал каждую строку с id = 10 для дополнительной защиты MVCC, выполнив SELECT ... FOR UPDATE. Его ответ еще более подчеркивает мысль, которую Джек и я говорили все это время: 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

Я не верю, что есть способ объединить все три утверждения. Самая близкая вещь к этому не очень вам помогает, и это SET SELECT. Ваша лучшая ставка - это триггер. Ниже приведен пример триггера, который я часто использую для поддержки именно такого контрольного журнала (созданного с помощью 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: это прекрасно работает как один запрос, когда я отправляю на сервер БД из кода приложения, где я рассматриваю этот набор как один запрос. почему ты так говоришь?. Можете ли вы опровергнуть это по веским причинам?
Сараванан

2
@saravanan: В глазах InnoDB или любой ACID-совместимой СУБД (Oracle, SQLServer, PostreSQL, Sybase и т. д.) эти два оператора SQL невозможно назвать одним запросом. Как ACID-совместимая база данных будет рассматривать их как два оператора. По умолчанию в InnoDB включен автокоммит. Первый оператор, INSERT, будет выполнен как отдельная транзакция. Данные Multiversioning Concurrency Control (MVCC) будут сгенерированы, чтобы хранить копию исходных данных в таблице t2 построчно. Если MySQL дает сбой во время выполнения INSERT, InnoDB использует данные MVCC для отката t2 в исходное состояние.
RolandoMySQLDBA

1
@saravanan: Предположим, INSERT успешно сработал. Данные, полученные в результате операции INSERT, были зафиксированы (с включенным автоматическим подтверждением), и защитная таблица MV2 t2 отбрасывается. Когда вы выполняете UPDATE, MVCC генерируется для таблицы t1, и выполняется UPDATE. Если MySQL дает сбой во время UPDATE, InnoDB использует данные MVCC в t1 для отката UPDATE. Даже если ОБНОВЛЕНИЕ изменяет только одну строку, существует возможность «один в миллион» перемещать записи из t1 в t2 с идентификатором 10 и не изменять идентификатор 10 на идентификатор 11 в t1. Чтобы предотвратить этот уникальный сценарий, вам нужно сделать следующее ...
RolandoMySQLDBA

@savaranan: рассматривать два оператора SQL как одну транзакцию. Простой способ сделать это: НАЧАЛО; INSERT INTO t2 ВЫБРАТЬ * ОТ t1 ГДЕ id = 10; ОБНОВЛЕНИЕ t1 SET id = 11 ГДЕ id = 10; COMMIT; Самой сильной причиной для обработки двух операторов SQL как одной транзакции является тот факт, что MVCC, созданный для INSERT, будет существовать в течение ОБНОВЛЕНИЯ. Если сбой MySQL должен произойти во время UPDATE внутри транзакции (BEGIN; ... COMMIT; блок), MVCC откатит все изменения в согласованное состояние. Если и INSERT, и UPDATE завершены, MVCC сбрасывается в последний момент.
RolandoMySQLDBA
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.