RDBM - 一对一 - >检查条目是否存在并插入/更新或始终删除并仅插入?

时间:2016-07-07 16:54:35

标签: database performance sql-update sql-insert one-to-one

不幸的是,我不知道如何命名这个问题,所以如果你有更好的建议,请继续编辑: - )

多年来,我一直在尝试插入/更新一对一资源时使用此方法:执行DELETE以确保没有具有此类PK的行,然后执行INSERT只要。我一直认为这是性能和简单性的最佳案例。在谈论性能时我的意思是双方 - 数据库和应用程序层(即执行DELETE查询似乎比执行SELECT并检查结果更便宜,同时考虑到有数据存在转移了两个方向)。

但当然还有其他方法,如INSERT ... ON DUPLICATE KEY UPDATE ...IF EXISTS (SELECT ...) UPDATE ... ELSE INSERT...UPDATE ... ;IF ROWCOUNT = 0 INSERT ...(取决于底层的RDBMS),当然在应用层执行相同的操作,即首先检查该条目是否存在以及是否确实执行UPDATEINSERT否则(或执行UPDATE并检查受影响的行数,如果为零,则执行INSERT [带来另一个复杂因素是,如果UPDATE没有更改底层资源,它也会因为受影响的行数返回零,因此INSERT之后将返回重复的PK错误])...

我现在很好奇最好的方法是什么?最好的意思是,如果你考虑表现,最佳实践等......

1 个答案:

答案 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);