不幸的是,我不知道如何命名这个问题,所以如果你有更好的建议,请继续编辑: - )
多年来,我一直在尝试插入/更新一对一资源时使用此方法:执行DELETE
以确保没有具有此类PK的行,然后执行INSERT
只要。我一直认为这是性能和简单性的最佳案例。在谈论性能时我的意思是双方 - 数据库和应用程序层(即执行DELETE
查询似乎比执行SELECT
并检查结果更便宜,同时考虑到有数据存在转移了两个方向)。
但当然还有其他方法,如INSERT ... ON DUPLICATE KEY UPDATE ...
,IF EXISTS (SELECT ...) UPDATE ... ELSE INSERT...
或UPDATE ... ;IF ROWCOUNT = 0 INSERT ...
(取决于底层的RDBMS),当然在应用层执行相同的操作,即首先检查该条目是否存在以及是否确实执行UPDATE
,INSERT
否则(或执行UPDATE
并检查受影响的行数,如果为零,则执行INSERT
[带来另一个复杂因素是,如果UPDATE
没有更改底层资源,它也会因为受影响的行数返回零,因此INSERT
之后将返回重复的PK错误])...
我现在很好奇最好的方法是什么?最好的意思是,如果你考虑表现,最佳实践等......
答案 0 :(得分:3)
使用ON DUPLICATE KEY UPDATE
是唯一理智的事情;它更简单,意图很明确 - 它易于阅读。它也会表现得更好,因为索引条目不需要更改。
如果两个进程尝试同时更新同一个密钥,则使用DELETE
然后INSERT
容易受到竞争条件的影响,这会导致其中一个进程出现唯一的密钥冲突。它也慢得多,因为不仅必须在物理上删除一行并插入新行,还必须删除索引条目,然后插入。
使用IF EXISTS
也不是一个好的选择,因为您必须在存储过程中执行此操作,因此它将锁定到该调用选项,并且无法移植到应用程序中。此外,它只是尝试复制ON DUPLICATE KEY UPDATE
内置命令,因此它永远不会那么高效。
回应关于程序员表现的评论,即效率,恕我直言也非常重要。
如果您想避免重复参数,请为方便起见添加一些重构:
void applyTwice(PreparedStatement stmt, int fromIndex, Object... values) {
for (int i = 0; i < values.length; i++) {
stmt.setObject(i + fromIndex, values[i]);
stmt.setObject(i + fromIndex + values.length, values[i]);
}
}
将其称为:
applyTwice(stmt, 3, "foo", "bar", 99);
将有效地做到这一点:
stmt.setObject(3, "foo");
stmt.setObject(4, "bar");
stmt.setObject(5, 99);
stmt.setObject(6, "foo");
stmt.setObject(7, "bar");
stmt.setObject(8, 99);
fromIndex
参数允许在查询中不重复的非重复参数,如ID等。
您还可以创建一个在多个索引处应用单个值的简单方法:
void apply(PreparedStatement stmt, Object value, int... indexes) {
for (int i = 0; i < indexes.length; i++) {
stmt.setObject(indexes[i], value);
}
}
您可以这样称呼:
apply(stmt, "foo", 3, 6);