consulta sobre la combinación de una actualización y una consulta de inserción en una sola consulta en mysql

9

Quiero hacer un seguimiento del historial de cambios para un usuario, de modo que cada vez que cambie su perfil, necesito tomar los datos antiguos y almacenarlos en el historial y actualizarlos con datos nuevos.

Puedo usar un selectpara obtener los datos antiguos, un inserthistorial y finalmente un updatecambio de datos.

¿Puedo tener todo esto en una sola consulta en MySQL sin utilizar procedimientos almacenados, disparadores, etc. como el uso de bloqueos, etc. Si es así, dame una pequeña muestra.

Saravanan
fuente
1
@savaranan: esta pregunta es digna de un +1 porque presenta un fuerte recordatorio a los DBA y desarrolladores para usar transacciones y aprovechar al máximo las propiedades ACID de la base de datos.
RolandoMySQLDBA
2
@savaranan: Para todos los efectos, Jack proporcionó la ÚNICA respuesta plausible que existe. De hecho, Jack Douglas dio un paso adicional y forzó un bloqueo intermitente en cada fila con id = 10 para mayor protección de MVCC haciendo SELECT ... PARA ACTUALIZAR. Su respuesta acentúa aún más el punto que Jack y yo hemos estado diciendo todo el tiempo: una ACTUALIZACIÓN e INSERTAR no pueden ser, ni ser, una sola consulta, solo pueden ser una sola transacción para el comportamiento SQL que su pregunta propone.
RolandoMySQLDBA

Respuestas:

13

Para hacer esto sin el riesgo de bloqueo de otro usuario intenta actualizar el mismo perfil, al mismo tiempo, es necesario bloquear la fila en t1primer lugar, a continuación, utilizar una transacción (como señala Rolando en los comentarios a su pregunta):

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;
Jack dice que intente topanswers.xyz
fuente
Esto es simplemente genial para bloquear aún más cada fila con id = 10. Eso debería ser un +2. Todo lo que puedo dar es un +1 !!!
RolandoMySQLDBA
1

No creo que haya una manera de combinar las tres declaraciones. Lo más parecido a eso realmente no te ayuda, y eso es un SET SELECT. Tu mejor apuesta es un disparador. A continuación se muestra una muestra de un desencadenante que utilizo a menudo para mantener tal seguimiento de auditoría (construido con 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 ;';
Bryan Agee
fuente
-3

He descubierto que esta consulta funciona en servidores SQL y MySQL INSERT INTO t2 SELECT * FROM t1 WHERE id=10; UPDATE t1 SET id=11 WHERE id=10;

Espero que esto sea útil para alguien más también en el futuro.

Saravanan
fuente
44
Esto no es realmente una consulta. En realidad, se trata de dos consultas que deben tratarse como una transacción.
RolandoMySQLDBA
@rolandomysqldba: esto funciona bien como una consulta única cuando envío a un servidor db desde el código de la aplicación, donde trato este conjunto como una consulta única. ¿Por qué lo dices?. ¿Puedes refutar esto con fuertes razones ..
Saravanan
2
@saravanan: a los ojos de InnoDB o cualquier RDBMS compatible con ACID (Oracle, SQLServer, PostreSQL, Sybase, etc.), es imposible llamar a esas dos declaraciones SQL una consulta. Como una base de datos compatible con ACID los trataría como dos declaraciones. Por defecto, InnoDB tiene la confirmación automática activada. La primera instrucción, INSERT, se ejecutaría como una sola transacción. Los datos de Control de concurrencia de multiversionamiento (MVCC) se generarían para mantener una copia de los datos originales en la tabla t2 fila por fila. Si MySQL falla durante la ejecución de INSERT, InnoDB usa los datos de MVCC para revertir t2 a su estado original.
RolandoMySQLDBA
1
@saravanan: Supongamos que INSERT funcionó correctamente. Los datos que resultaron del INSERT se han confirmado (con la confirmación automática activada) y la tabla de protección MVCC t2 se descarta. Cuando realiza la ACTUALIZACIÓN, se genera MVCC contra la tabla t1 y se realiza la ACTUALIZACIÓN. Si MySQL falla durante la ACTUALIZACIÓN, InnoDB usa los datos de MVCC en t1 para deshacer la ACTUALIZACIÓN. Incluso si la ACTUALIZACIÓN cambia solo una fila, existe la posibilidad de uno en un milloion de mover registros de t1 a t2 con id 10 y no cambiar id 10 a id 11 en t1. Para evitar este escenario único, debe hacer lo siguiente ...
RolandoMySQLDBA
@savaranan: trate las dos declaraciones SQL como una sola transacción. La manera simple de hacer esto es: COMENZAR; INSERTAR EN t2 SELECCIONAR * DESDE t1 DONDE id = 10; ACTUALIZAR t1 SET id = 11 DONDE id = 10; COMETER; La razón más poderosa para tratar las dos declaraciones SQL como una sola transacción es el hecho de que el MVCC creado para INSERT permanecería en existencia durante la ACTUALIZACIÓN. Si ocurriera un bloqueo de MySQL durante la ACTUALIZACIÓN dentro de una transacción (COMENZAR; ... COMPROMISO; bloquear) MVCC revertiría todos los cambios a un estado coherente. Si se completan INSERTAR y ACTUALIZAR, MVCC se descarta en el último momento.
RolandoMySQLDBA