3

我正在寻找一种方法,仅在 MySQL 中不存在该行时插入,并在该行存在且现有行的版本小于(或等于)新行的版本时更新。

例如,表定义为:

CREATE TABLE documents (
  id VARCHAR(64) NOT NULL,
  version BIGINT UNSIGNED NOT NULL,
  data BLOB,
  PRIMARY KEY (id)
);

并包含以下数据:

id  version  data
----------------------------
1   3        first data set
2   2        second data set
3   5        third data set

我想合并下表(更新:id 列是唯一的):

id  version  data
----------------------------
1   4        updated 1st
3   3        updated 2nd
4   1        new 4th

它应该产生以下内容(更新:查看如何仅更新 1 并插入 4):

id  version  data
----------------------------
1   4        updated 1st
2   2        second data set
3   5        third data set
4   1        new 4th

我看过INSERT ... ON DUPLICATE KEY UPDATE ...语句,但它不允许某种 WHERE 子句。另外,我不能真正使用REPLACE,因为它也不允许 WHERE。这甚至可以通过单个 MySQL 语句实现吗?

我正在使用 Java,并尝试使用带有批处理的PreparedStatement (addBatch) 插入/更新许多记录。任何帮助,将不胜感激。

更新:有没有办法将此查询与 Java 中的 PreparedStatement 一起使用?我有一个包含 ID、版本和数据的 Document 对象列表。

4

2 回答 2

3

编辑:在我之前的回答中,我建议在 上需要一个唯一约束(id, version),但实际上这不是必需的。您对 only 的唯一约束id足以使解决方案发挥作用。


您应该能够REPLACE按如下方式使用该命令:

REPLACE INTO main 
SELECT  IFNULL(m.id, s.id) id, 
        IFNULL(m.version, s.version) version, 
        IFNULL(m.data, s.data) data
FROM       secondary s
LEFT JOIN  main m ON (m.id = s.id AND m.version > s.version);

测试用例:

CREATE TABLE main ( 
   id int, 
   version int, 
   data varchar(50), 
   PRIMARY KEY (id)
);

CREATE TABLE secondary (id int, version int, data varchar(50));

INSERT INTO main VALUES (1, 3, 'first data set');
INSERT INTO main VALUES (2, 2, 'second data set');
INSERT INTO main VALUES (3, 5, 'third data set');

INSERT INTO secondary VALUES (1, 4, 'updated 1st');
INSERT INTO secondary VALUES (3, 3, 'udated 2nd');
INSERT INTO secondary VALUES (4, 1, 'new 4th');

结果:

SELECT * FROM main;
+----+---------+-----------------+
| id | version | data            |
+----+---------+-----------------+
|  1 |       4 | updated 1st     |
|  2 |       2 | second data set |
|  3 |       5 | third data set  |
|  4 |       1 | new 4th         |
+----+---------+-----------------+
4 rows in set (0.00 sec)

作为旁注,为了帮助您了解该REPLACE命令中发生的情况,请注意以下几点:

SELECT     s.id s_id, s.version s_version, s.data s_data,
           m.id m_id, m.version m_version, m.data m_data
FROM       secondary s
LEFT JOIN  main m ON (m.id = s.id AND m.version > s.version);

+------+-----------+-------------+------+-----------+----------------+
| s_id | s_version | s_data      | m_id | m_version | m_data         |
+------+-----------+-------------+------+-----------+----------------+
|    1 |         4 | updated 1st | NULL |      NULL | NULL           |
|    3 |         3 | udated 2nd  |    3 |         5 | third data set |
|    4 |         1 | new 4th     | NULL |      NULL | NULL           |
+------+-----------+-------------+------+-----------+----------------+
3 rows in set (0.00 sec)

然后,IFNULL()如果存在主表中的最新版本,则函数负责“覆盖”主表中的最新版本,如 id=3,版本=5 的情况。因此如下:

SELECT  IFNULL(m.id, s.id) id, 
        IFNULL(m.version, s.version) version, 
        IFNULL(m.data, s.data) data
FROM       secondary s
LEFT JOIN  main m ON (m.id = s.id AND m.version > s.version);

+------+---------+----------------+
| id   | version | data           |
+------+---------+----------------+
|    1 |       4 | updated 1st    |
|    3 |       5 | third data set |
|    4 |       1 | new 4th        |
+------+---------+----------------+
3 rows in set (0.00 sec)

上面的结果集仅包含来自辅助表的记录,但如果这些记录中的任何一条恰好在主表中有更新版本,则该行将被主表中的数据覆盖。这是我们提供REPLACE命令的输入。

于 2010-09-14T01:59:09.497 回答
2

我认为 INSERT ON DUPLICATE KEY UPDATE 是您最好的选择。你可以像这样使用它

INSERT INTO table1 SELECT * FROM table2 ON DUPLICATE KEY UPDATE table1.data=IF(table1.version > table2.version, table1.data, table2.data), table1.version=IF(table1.version > table2.version, table1.version, table2.version)

未经测试的语法,但我相信这个想法应该可行。

于 2010-09-14T01:37:13.057 回答