2009-07-31 32 views
15

Me gustaría tener un disparador para realizar siguiente operación de registros insertados:MySQL gatillo para actualizar un campo con el valor de Identificación del

# pseudocode 
if new.group_id is null 
    set new.group_id = new.id 
else 
    # don't touch it 
end 

Más claramente: decir que tengo una tabla con tres columnas: id primaria clave, group_id int, value varchar.

cuando inserto con group_id así:

INSERT INTO table(value, group_id) VALUES ('a', 10) 

me gustaría tener:

id | group_id | value 
---+----------+------ 
1 |  10 | a 

pero cuando Omito group_id:

INSERT INTO table(value) VALUES ('b') 

Cabe automáticamente configurado al id de este registro:

id | group_id | value 
---+----------+------ 
2 |  2 | b 

¿Es posible con un gatillo? (Sé que puedo actualizar el registro después de insertar pero tener el gatillo sería más agradable.)

Respuesta

25

No conozco ninguna forma de hacer esto en una declaración, incluso con un disparador.

La solución disparador que @Lucky sugirió se vería así en MySQL:

CREATE TRIGGER MyTrigger BEFORE INSERT ON MyTable 
FOR EACH ROW BEGIN 
    SET NEW.group_id = COALESCE(NEW.group_id, NEW.id); 
END 

Sin embargo, hay un problema. En la fase BEFORE INSERT, el valor id autogenerado aún no se ha generado. Así que si group_id es nulo, el valor predeterminado NEW.id que es siempre 0.

Pero si cambia el gatillo para disparar durante la fase de AFTER INSERT, por lo que tiene acceso al valor generado de NEW.id, no puede modificar los valores de columna .

MySQL no admite expresiones para el DEFAULT de una columna, por lo que tampoco puede declarar este comportamiento en la definición de la tabla.

La única solución es hacer el INSERT, y luego hacer inmediatamente un UPDATE para cambiar el group_id si no está configurado.

INSERT INTO MyTable (group_id, value) VALUES (NULL, 'a'); 
UPDATE MyTable SET group_id = COALESCE(group_id, id) WHERE id = LAST_INSERT_ID(); 
+0

Eso apesta. Incluso en 2016 –

+0

@AA_PV, esto podría ser resuelto por un objeto de secuencia independiente, como lo hacen en Oracle. Esta ha sido una solicitud de función abierta en MySQL desde 2003: https://bugs.mysql.com/bug.php?id=1625 Pero en Oracle, para usar la secuencia, * debe * llamarla ya sea en el código de la aplicación, que significa escribir código para * cada * INSERTAR en una tabla que desea tener una clave primaria de autointegrinación. O escriba un desencadenador para * cada * dicha tabla. Los usuarios de MySQL disfrutan de poder omitir la escritura de tantos activadores, y simplemente declaran 'AUTO_INCREMENT' como una opción de columna. –

+0

sí, pero dos columnas AUTO_INCREMENT no están permitidas en MySQL, ¿verdad? Aunque creo que se puede hacer algo similar a http://stackoverflow.com/a/7345183/4411645. Y para evitar la consulta anidada, tal vez también se pueda mantener en el código de la aplicación, aunque podría ser aún más inseguro, no estoy seguro. –

0

creo que esto va a funcionar para usted

que tiene dos tablas

test_b: a_id, value 
test_c: a_id, value 

Y aquí es una desencadenar en la inserción de la prueba b. Se comprueba si A_ID es nulo y si lo es, inserta 0

CREATE TRIGGER test_b AFTER INSERT ON test_b 
    FOR EACH ROW 
    INSERT INTO test_c (a_id, value) VALUES (IFNULL(NEW.a_id, 0),NEW.value) 
+0

Si me dices más sobre la estructura de tus tablas, puedo modificar esta consulta para que funcione para ti. –

-2

Este disparador debe hacer lo que le pida.

CREATE TRIGGER mytrigger BEFORE INSERT ON mytable 
      IF new.group_id IS NULL 
      SET new.group_id = new.id 
      END IF 
     END; 

se copia de un ejemplo muy similar en la página MYSQL documentation.

+0

No funciona en MySQL: Tiene un error en la sintaxis SQL; revise el manual que corresponde a su versión del servidor MySQL para la sintaxis correcta para usar cerca de 'WHERE new.group_id IS NULL END' en la línea 1 – szeryf

+0

. Debe poder obtener la sintaxis exacta de los ejemplos en http: //dev.mysql .com/doc/refman/5.1/es/create-trigger.html - No hice la prueba. Voy a intentar una edición – Lucky

-2

Un disparador parece excesivo en esta situación. Simplemente aplica un valor predeterminado.

CREATE TABLE `test` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `value` varchar(100) NOT NULL, 
    `group_id` TINYINT(3) UNSIGNED NOT NULL DEFAULT '2' 
) 
+0

Lo siento pero no entendiste el punto. No se trata de insertar el valor literal '2' como' group_id'. Quiero el 'id' de este registro como' group_id' a menos que especifique 'group_id' explícitamente. – szeryf

0

estoy respondiendo aquí, como en la respuesta aceptada Bill Karwin estados:

En la fase ANTES DE INSERTAR, el valor id autogenerado no ha sido aún generado. Así que si group_id es nulo, el valor predeterminado es NEW.id que es siempre 0.


tengo una respuesta para ella - para OP (y los visitantes por venir), aquí están algunos puntos: no se puede actualizar la tabla desde la que el gatillo se invoca, por lo que obtendrá Error 1442:

Error Code: 1442 
Can't update table 'MyTable' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. 

1.to actualización de la nueva fila uso BEFORE INSERT ON gatillo, de esta manera se puede actualizar todos los campos para la nueva fila, que pueden ser accesibles a través del operador es decir NUEVO

set NEW.group_id = NEW.id 

2.Get valor AUTO_INCREMENT antes de insertar:

SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='MyTable' 

Para resumir up - el SQL de activación para que sea algo como el siguiente:

DELIMITER // 
DROP TRIGGER IF EXISTS MyTrigger// 
CREATE TRIGGER MyTrigger BEFORE INSERT ON MyTable 
FOR EACH ROW BEGIN 
    IF new.group_id IS NULL 
     set @auto_id := (SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES 
         WHERE TABLE_NAME='MyTable' AND TABLE_SCHEMA=DATABASE()); 
     set NEW.group_id = @auto_id; 
    ENF IF; 
END; 
// 
DELIMITER ; 
+0

Al seleccionar el valor auto_increment de la tabla, se introduce una condición de carrera. –

Cuestiones relacionadas