ROLLBACK no funciona después de INSERTAR EN la tabla de destino recién creada


11

Estoy trabajando en un script PHP que importa el archivo CSV ( customers.csv) en la tabla MySQL ( customers).

Antes de insertar el contenido del archivo CSV en la tabla mysql, primero estoy haciendo una copia de seguridad de la customerstabla original .

Estoy envolviendo todo el proceso de importación (incluida la copia de seguridad) en una transacción mysql (para tener en cuenta los casos en que CSV está dañado en algún punto intermedio y para garantizar que la importación sea atómica).

El problema es que ROLLBACK no parece funcionar cuando lo llamo justo después de la INSERT INTOdeclaración: cuando verifico la base de datos a través de phpMyAdmin, puedo ver la tabla recién creada Y LAS FILAS DENTRO DE ELLA todavía están presentes después del roollback .

Aquí está el registro de las operaciones:

[2015-01-19 14:08:11] DEBUG: "START TRANSACTION" [] []
[2015-01-19 14:08:11] DEBUG: SHOW TABLES LIKE :table_name; [] []
[2015-01-19 14:08:28] DEBUG: CREATE TABLE `customers__20150119_14_08_20` LIKE `customers` [] []
[2015-01-19 14:08:37] DEBUG: INSERT INTO `customers__20150119_14_08_20` SELECT * FROM `customers` [] []
[2015-01-19 14:08:50] DEBUG: "ROLLBACK" [] []

Entonces, me pregunto por qué ROLLBACKse llama depsite , la transacción no se cancela. Entiendo que CREATE TABLEno es de naturaleza transaccional y no se puede revertir. Pero suponía que INSERT INTOdebido a que se trata de insertar filas (sin definir el esquema), SERÁ realmente transaccional, y después de ROLLBACK me quedaré con la tabla de destino vacía. ¿Por qué no es el caso?

Y aquí está la salida SHOW CREATE TABLE customers(entonces mi tabla es InnoDb):

CREATE TABLE `customers` (
 `Code` varchar(32) NOT NULL,
 `Name` varchar(128) DEFAULT NULL,
 `Price` varchar(128) DEFAULT NULL,
 PRIMARY KEY (`Code`),
 KEY `Price` (`Price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

y aquí está la salida para la tabla de destino:

CREATE TABLE `customers__20150119_14_08_20` (
 `Code` varchar(32) NOT NULL,
 `Name` varchar(128) DEFAULT NULL,
 `Price` varchar(128) DEFAULT NULL,
 PRIMARY KEY (`Code`),
 KEY `Price` (`Price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

¿Es el mismo comportamiento si reordena primero create table, entonces start transaction, insert, rollback?
ypercubeᵀᴹ

Estaba a punto de decir eso a !!!
RolandoMySQLDBA

¿Deshabilita la confirmación automática en la conexión en su programa?
mustaccio

Respuestas:


13

La razón es que algunas declaraciones, como CREATE TABLEcausar una confirmación implícita. Puede leer sobre ellos en la documentación: Declaraciones que causan un compromiso implícito .

Entonces la secuencia original de declaraciones:

START TRANSACTION
SHOW TABLES LIKE customers
CREATE TABLE `customers__20150119_14_08_20` LIKE `customers`
INSERT INTO `customers__20150119_14_08_20` SELECT * FROM `customers`
ROLLBACK

se expandirá a:

START TRANSACTION ;   -- transaction context created
SHOW TABLES LIKE customers ;

COMMIT ;              -- CREATE TABLE forces commit before itself
                      --     (at this point the previous transaction is done.)
START TRANSACTION ;   -- and a new transaction  
CREATE TABLE `customers__20150119_14_08_20` 
    LIKE `customers` ;
COMMIT ;              -- CREATE TABLE forces commit after itself. 
                      -- At this point there's no transaction context

START TRANSACTION ;   --  starts a new transaction
INSERT INTO `customers__20150119_14_08_20` 
    SELECT * FROM `customers` ;
COMMIT ;              -- caused by "autocommit on" setting (guess). 

ROLLBACK ;            -- this rollback HAS NOTHING to undo

La solución sería comenzar la transacción (o una nueva) después de la CREATE TABLEdeclaración o usar una tabla temporal.


@Dimitry, gracias por la edición.
ypercubeᵀᴹ

1
Y @RolandoMySQLDBA por sus amables palabras. Soy el FGITW hoy (y solo 15 segundos más rápido que tú;)
ypercubeᵀᴹ

@ypercube bienvenido! Me tomó un tiempo darme cuenta de dónde exactamente esta CREAT TABLE cause an implicit commit... Así que tuve que hacer este esquema en papel de todos modos :) @RolandoMySQLDBA gracias por su rápida aportación también. ¡He leído algunas docenas de sus respuestas en el último año y me ayudaron mucho!
Dimitry K

Entonces, ¿estás diciendo que el commit implícito antes del INSERT, causado por la instrucción DDL, también causa un commit después del insert?
mustaccio

1
Sí, hay dos partes en el razonamiento, pero la parte principal en mi opinión, que el OP no pudo entender fue el compromiso implícito de la tabla de creación.
ypercubeᵀᴹ

3

Parece que el orden de las declaraciones está causando el problema.

En mi antiguo bloque de la fila posterior dentro de la transacción ACID innodb , nombré 12 declaraciones que interrumpen una transacción de forma intermitente. En su caso particular, fue la CREATE TABLEdeclaración.

Una vez que corriste CREATE TABLEdentro de un START TRANSACTION... COMMIT/ROLLBACKbloque, no había marco para revertir.

Simplemente ejecute el CREATE TABLEantes START TRANSACTIONy debería estar bien.

Darle una oportunidad !!!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.