【问题标题】:RDBM - one to one -> check if entry exists and insert/update OR always delete and only insert?RDBM - 一对一 - >检查条目是否存在并插入/更新或总是删除并且只插入?
【发布时间】:2016-07-07 16:54:35
【问题描述】:

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

多年来,我在尝试插入/更新一对一资源时一直在使用这种方法:执行 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 错误])...

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

【问题讨论】:

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


    【解决方案1】:

    使用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);
    

    【讨论】:

    • 好吧,对我来说似乎是合理的,尤其是在谈论性能时。但它有一个背景——如果我准备一个语句然后执行它同时提供所有变量作为参数,使用ON DUPLICATE KEY UPDATE我将不得不复制它们,导致例如20 个参数而不是 10 个......但我的目标是性能,所以多输入一点是值得的 :-)
    • @shadyyx 我已经为你的打字次数增加了一些可能的缓解
    • 好的,感谢您提供代码示例,但我必须提供完全不同的解决方案,因为在我的情况下(GoLang)没有单独设置命名参数(即stmt.setParam(),而是通过传递给exec(),即stmt.Exec(param1, param2, param3, ..., paramX))。但是可以只制作重复值的 一个数组(在 Go 中是 slice),然后以这种方式传递它们 stmt.Exec(params...)(字面意思是这三个点)。然而,INSERT ... ON DUPLICATE KEY UPDATE ... 如何遵守 REST PUT - 即资源完全被 PUT 请求替换
    • @shady 它符合 PUT。数据库实体(行)被有效替换。从技术上讲,物理行可能会被重用,但这是一个实现细节。结果通过了 PUT 的所有测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-27
    相关资源
    最近更新 更多