Java 在MySQL中,如何仅在行不';t仅当现有版本较少时才存在和更新

Java 在MySQL中,如何仅在行不';t仅当现有版本较少时才存在和更新,java,mysql,insert,replace,insert-update,Java,Mysql,Insert,Replace,Insert Update,我正在寻找一种方法,只在MySQL中不存在行时插入,并在存在行且现有行的版本小于(或等于)新行的版本时更新 例如,该表定义为: CREATE TABLE documents ( id VARCHAR(64) NOT NULL, version BIGINT UNSIGNED NOT NULL, data BLOB, PRIMARY KEY (id) ); 并包含以下数据: id version data ---------------------------- 1 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
我想合并下表(UPDATE:id列是唯一的):

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

我已经看过了语句,但它不允许使用某种WHERE子句。此外,我不能真正使用,因为它也不允许在哪里。用一条MySQL语句就可以做到这一点吗

我正在使用Java,并尝试使用with batch(addBatch)插入/更新许多记录。任何帮助都将不胜感激


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

我认为重复密钥更新时插入是最好的选择。你可以像这样使用它

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)

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

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


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

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)
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)
然后,
IFNULL()
函数负责从主表中“覆盖”最新版本(如果存在),如id=3,version=5。因此:

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)
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
命令。

您如何知道何时添加不存在的行?对不起,我忘了提到id是唯一的。但是
id
是自动递增的,不是吗?这个问题仍然有待抽象,以便我知道何时应该进行插入或更新,对不起。不,id不是自动递增的,事实上,它是一个varchar列。我使用了两个表作为示例,但实际上我没有两个表。我有一个文档对象列表,其中包含以下字段:id、版本和数据。我正试图通过Java中的PreparedStatements找出这样做的语法。然后去掉table2引用,并将插入值的地方放在?s中。@Todd,我认为你的答案与我实际寻找的最接近。我将对你的答案和丹尼尔的答案进行测试,看看哪一个能产生更好的性能结果。@Mohamed:你说得对,我认为这更快。我忽略了这样一个事实,即
REPLACE
在重新插入行之前会删除该行。@Daniel,测试性能似乎没有意义,因为我还必须添加创建临时表、填充临时表以及将其与主表内部联接所需的时间。好的Daniel,还有一个问题。如何在不需要辅助表的情况下执行相同的查询?@Mohamed:如果不需要,要合并的数据将保存在哪里?@Mohamed:您可能需要辅助表:
创建临时辅助表(id int,…)。。。然后
INSERT
将应用程序中的数据插入临时辅助表,并应用
REPLACE
功能。会话终止时会自动删除临时表。@Daniel,假设我有一个
列表
,其中包含
id
版本
、和
数据
成员,并且我想使用
PreparedStatement.addBatch()
。。我想要一个简单的语句(
如果不存在,则插入…,否则如果orig.version我对临时表的性能不是很确定。我基本上会在3MM行的区域插入一些东西…我不知道临时表是否能够处理它。但我会设置一个性能测试,看看它是否可以接受。谢谢!