при условии обновления дубликата ключа - PullRequest
12 голосов
/ 25 июня 2009

Я пытаюсь вставить новую строку, но если ключ уже существует, я хочу обновить строку ТОЛЬКО, если в таблице есть другое определенное значение. Возможно ли это в запросе / заявлении mysql?

Моя таблица состоит из следующих столбцов: шапка, варежки, имя, last_update

шапка + варежки составляют уникальный индекс (скажем, значения для "шапка" и "варежки" - это цвета)

Предположим, это уже есть в таблице:

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

На новый ключ я хочу вставить как обычно. На дубликате ключа я хочу обновить ТОЛЬКО ЕСЛИ имя меняется, в противном случае игнорируем. Причина этого в том, что я хочу сохранить значение last_update (отметка времени).

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

Возможно ли это без использования отдельных операторов, чтобы сначала найти существующую строку, сравнить значения, а затем выпустить обновление, если это необходимо? Если так, какой будет синтаксис?


Спасибо за ваши ответы. Я перепробовал их все. На самом деле, используя просто простое выражение UPDATE, например

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

не приводит к обновлению строк. Но, используя либо

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

или

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;

каким-то образом приводит к обновлению строки (и метка времени изменилась).

FWIW, я использую таблицу:

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 версии 4.1.22 (возможно, это имеет значение?) Опять же, моя благодарность за все ответы.

Ответы [ 4 ]

17 голосов
/ 25 июня 2009

Вы можете использовать обычные конструкции sql в синтаксисе ON DUPLICATE KEY . Таким образом, чтобы сделать условные обновления во время вставки, вы можете сделать следующее:

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;

Это изменит значение на то, что вы указали в операторе вставки, когда оно отличается от того, что в строке, и установит значение, равное тому, которое уже есть, если оно не изменилось, и приведет к тому, что MySQL ничего не сделает для строка, сохраняющая метку времени last_update, как указал Кассной.

Если вы хотите на 100% убедиться, что вы не полагались на поведение MySQL, где он не обновляет строку, если вы устанавливаете значение для себя, вы можете сделать следующее, чтобы принудительно установить временную метку:

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;

Это обновит last_update до now() только после изменения имени, иначе MySQL сохранит значение last_update.

Кроме того, в разделе ON DUPLICATE KEY оператора вы можете ссылаться на столбцы в таблице по их имени, и вы можете получить значения, которые вы указали в разделе значений оператора вставки, используя VALUES (имя_ столбца) функция.


Ниже приведен журнал, который показывает, что последний предоставленный оператор работает даже на 4.1, где другие не работают из-за ошибки, исправленной в версии 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>

Дайте мне знать, если у вас есть какие-либо вопросы.

НТН,

-Dipin

3 голосов
/ 25 июня 2009

Вам нужно INSERT ... ON DUPLICATE KEY UPDATE синтаксис.

Ваш запрос будет выглядеть так:

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

Если у вас уже есть запись с синим / зеленым / george для hat / mittens / name, ОБНОВЛЕНИЕ фактически не будет выполнено, и ваша временная метка не будет обновлена. Однако, если у вас есть запись с синим / зеленым / бетти, то «бетти» будет перезаписана «георгом», а ваша метка времени будет обновлена.

0 голосов
/ 25 июня 2009

Если вы выполняете несколько INSERT (либо SELECT или предоставляете несколько строк для VALUES), вы можете сделать следующее:

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

Это будет:

  • Вставить строки, которые не являются дубликатами, соответственно. УНИКАЛЬНЫМ ПОКАЗАТЕЛЯМ
  • Обновите столбец name для остальных (когда новое значение отличается)

Предупреждение: комментарии в документации к синтаксису ON DUPLICATE говорят о том, что впоследствии результат mysql_affected_rows() не является надежным.

0 голосов
/ 25 июня 2009

Вам ничего не нужно делать, это поведение по умолчанию.

Если ваш запрос выбирает строку для UPDATE, но обновленное значение остается таким же, как в:

UPDATE  table
SET     col = col

, отметка времени также остается прежней.

Эта строка даже не считается затронутой, запрос вернет 0 rows affected.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...