2009-06-25 22 views
12

Estoy tratando de insertar una nueva fila, pero si la clave ya existe, quiero actualizar la fila SOLAMENTE si un cierto otro valor está en la tabla es diferente. ¿Es esto posible en una consulta/enunciado mysql?condicional en la actualización de clave duplicada

Mi mesa consisten en las siguientes columnas: gorro, guantes, nombre, last_update

sombrero + manoplas componen el índice único (dicen que los valores de "sombrero" y "manoplas" son colores)

vamos a suponer que esto ya está en la tabla:

 
1. hat=blue mittens=green name=george last_update=tuesday 
2. hat=red mittens=green name=bill last_update=monday 

en un duplicado de la llave, quiero insertar como de costumbre. En la clave duplicada, quiero hacer una actualización SÓLO SI el nombre cambia; de lo contrario, ignórelo. La razón para esto es que quiero conservar el valor last_update (timestamp).

 
hat=yellow mittens=purple name=jimmy -- insert new row 
hat=blue mittens=green name=george -- ignore 
hat=blue mittens=green name=betty -- update row 

¿Es esto posible sin el uso de declaraciones por separado para mirar por primera vez la fila existente, comparar los valores y luego emitir una actualización si es necesario? Si es así, ¿cuál sería la sintaxis?


Gracias por su respuesta. Intenté todos. De hecho, el uso de una simple instrucción UPDATE como

update tbl set name='george' where hat='blue' and mittens='green' 

no genera filas actualizadas. Sin embargo, utilizando

INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name='george'; 

o

INSERT INTO tbl (hat, mittens, name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name=CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END; 

alguna manera da lugar a la fila que se está actualizado (y la fecha haya cambiado).

Fwiw, esta es la tabla que estoy usando:

CREATE TABLE `tbl` (
`hat` varchar(11) default NULL, 
`mittens` varchar(11) default NULL, 
`name` varchar(11) default NULL, 
`stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, 
UNIQUE KEY `clothes` (`hat`,`mittens`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 

MySQL es la versión 4.1.22 (tal vez esto importa?) Una vez más, mi agradecimiento por todas las respuestas.

+0

yo supongo que es su versión de MySQL, que es el problema aquí. Parece que arreglaron un error (http://bugs.mysql.com/bug.php?id=28904) con INSERTAR ... EN ACTUALIZACIÓN DUPLICADA DE LLAVE en la bifurcación 5.0, después de finalizar el soporte activo para la rama 4.1 (Dic. 31 de diciembre de 2006). He vuelto a crear tu tabla y tu caso de prueba en mi máquina y funcionó como se esperaba, y estoy ejecutando MySQL Server 5.0.67. Si no puede actualizar su instalación de MySQL, entonces puede estar atascado sin una solución de 'sola consulta' a su problema. – zombat

+0

CONFIRMADO. Ambas soluciones funcionan en 5.0.45 (desafortunadamente, eso es solo un servidor de desarrollo). ¡Gracias de nuevo! – javalina

+0

¿Has probado la segunda de las dos consultas que sugerí?¿El que controló manualmente lo que sucedió con la columna de sello (last_update)? Creo que uno debería funcionar aunque exista esa falla, ya que explícitamente decide si actualizar la columna o no con otra declaración de caso. – Dipin

Respuesta

17

Puede usar construcciones sql normales en la sintaxis ON DUPLICATE KEY. Así que con el fin de hacer actualizaciones condicionales durante una inserción que puede hacer lo siguiente:

INSERT INTO tbl (hat, mittens, name) 
VALUES ('yellow','purple','jimmy') 
ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) 
            THEN VALUES(name) ELSE name END; 

Esto cambiará el valor a lo que ya ha proporcionado a la instrucción de inserción cuando es diferente de lo que hay en la fila y se establezca el valor ya sea lo que es si no ha cambiado y dará como resultado que MySQL no haga nada en la fila conservando la marca de tiempo last_update como lo señaló Quassnoi.

Si quería hacer 100% seguro de que no confiaban en el comportamiento de MySQL donde no actualiza una fila si se establece un valor que en sí puede hacer lo siguiente para forzar la marca de tiempo:

INSERT INTO tbl (hat, mittens, name) 
VALUES ('yellow','purple','jimmy') 
ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) 
            THEN VALUES(name) ELSE name END 
         , last_update = CASE WHEN name <> VALUES(name) 
             THEN now() ELSE last_update END; 

Esto sólo se actualizará el last_update a now() cuando el nombre ha cambiado de lo contrario le dirá MySQL para retener el valor de last_update.

Además, en la sección ON DUPLICATE KEY de la instrucción, puede consultar las columnas de la tabla por su nombre y obtener los valores que proporcionó a la sección Insertar valores de declaración utilizando la función VALUES(column_name).


El siguiente es un registro que muestra que la última declaración proporcionada funciona incluso en 4.1, donde los otros no funcionan debido a un error que fue corregido en la versión 5.0.

C:\mysql\bin>mysql -u root -p 
Enter password: 
Welcome to the MySQL monitor. Commands end with ; or \g. 
Your MySQL connection id is 1 to server version: 4.1.22-community 

Type 'help;' or '\h' for help. Type '\c' to clear the buffer. 

mysql> show databases; 
+----------+ 
| Database | 
+----------+ 
| mysql | 
| test  | 
+----------+ 
2 rows in set (0.00 sec) 

mysql> use test; 
Database changed 
mysql> show tables; 
Empty set (0.00 sec) 

mysql> CREATE TABLE `tbl` (
    -> `hat` varchar(11) default NULL, 
    -> `mittens` varchar(11) default NULL, 
    -> `name` varchar(11) default NULL, 
    -> `stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, 
    -> UNIQUE KEY `clothes` (`hat`,`mittens`) 
    ->) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
Query OK, 0 rows affected (0.01 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george'); 
Query OK, 1 row affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:16 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name='george'; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:30 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat, mittens, name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name=CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:42 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END, stamp = CASE WHEN name <> VALUES(name) THEN now() ELSE stamp END; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:42 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> 

Déjeme saber si usted tiene alguna pregunta.

HTH,

-Dipin

+3

Para mí fue un poco difícil averiguar qué significa <> (¡no igual!), Así que pensé en agregarlo aquí en un comentario en caso de que alguien más se lo pregunte en el futuro. :) – Alisso

+0

Hola pequeña ayuda ¿cómo puedo consultar cuando tengo varias columnas al actualizar – Chaitanya

0

No necesita hacer nada, este es el comportamiento predeterminado.

Si su consulta selecciona una fila para UPDATE pero el valor actualizado se mantiene el mismo, como en:

UPDATE table 
SET  col = col 

, la marca de tiempo también sigue siendo el mismo.

Esta fila ni siquiera cuenta como afectada, la consulta devolverá 0 rows affected.

3

Necesita la sintaxis INSERT ... ON DUPLICATE KEY UPDATE.

Su consulta se vería así:

INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') 
    ON DUPLICATE KEY UPDATE name='george'; 

Si tuviera un registro con azul/verde/George para el sombrero/mitones/nombre ya, no hay actualización en realidad llevar a cabo, y su marca de tiempo no sería actualizado. Sin embargo, si tiene un registro con azul/verde/betty, entonces 'betty' se sobrescribirá con 'george', y su marca de tiempo se actualizará.

0

Si va a realizar un múltiplo INSERT (ya sea por SELECT o proporcionar múltiples filas a VALUES), puede hacer lo siguiente:

INSERT INTO tbl (hat,mittens,name) VALUES 
    ('yellow','purple','jimmy'), 
    ('blue','green','george'), 
    ('blue','green','betty') 
ON DUPLICATE KEY UPDATE name = VALUES(name); 

Esto hará lo siguiente:

  • Insertar filas que aren' t duplicados con resp. a los índices UNIQUE
  • Actualización de la columna de la name para los demás (cuando el nuevo valor es diferente)

Advertencia: los comentarios en el documentation on the ON DUPLICATE syntax dice que el resultado mysql_affected_rows() después no es fiable.

Cuestiones relacionadas