我可以使用 MERGE INTO 在 Apache Derby 中模拟“upsert”吗?

Posted

技术标签:

【中文标题】我可以使用 MERGE INTO 在 Apache Derby 中模拟“upsert”吗?【英文标题】:Can I use MERGE INTO to simulate "upsert" in Apache Derby? 【发布时间】:2016-04-16 12:52:18 【问题描述】:

我们正在使用 Derby,并且有很多这样的代码:

try (ResultSet rs = executeQuery(...)) 
    if (rs.next()) 
        updateRowSet(rs, ...);
        rs.updateRow();
     else 
        executeUpdate(...);
    

过去,我们正在寻找一种在服务器端执行此逻辑的方法,发现某些数据库支持“upsert”(更新或插入)操作。 Derby 有一个对MERGE INTO 的功能请求,这应该是 SQL:2003 标准的执行方式,所以我们坐下来看着票,很长一段时间过去了。

Derby 10.11 终于添加了MERGE INTO。还没有人有时间检查并更新代码以使用它,但是在阅读他们的文档时,他们的所有示例都显示从一个表合并到另一个表。但是等一下,我们的数据还没有在表格中!

我知道我可以它放在一个表中,但是它又是多个查询,这完全违背了使用它的意义。

我确定可以在将其放入表格的情况下做到这一点,但由于文档没有显示单个示例,我不确定如何继续。

这是我一直在尝试的:

try (PreparedStatement ps = connection.prepareStatement(
        "MERGE INTO things AS target " +
        // Awkward point 1:
        // It wants a "source" table, but I don't have one.
        // So I thought I would try to use the same table with
        // another name.
        "  USING things AS source ON target.id = ?" +
        "  WHEN MATCHED THEN" +
        "    UPDATE SET data = ?" +
        "  WHEN NOT MATCHED THEN" +
        "    INSERT (id, data) VALUES (??, ?)"))

    ps.setLong(1, id);
    ps.setBinaryStream(2, data);
    ps.setLong(3, id);
    // Awkward point 2:
    // Passing an InputStream into a query as two
    // parameters.
    ps.setBinaryStream(4, data);
    ps.execute();

这似乎没有做任何插入,但也没有给出错误,所以我完全没有什么可继续的。

【问题讨论】:

可能有用的相关问题:***.com/questions/11216067/… 和 ***.com/questions/2479488/… 我认为德比不可能做到这一点。它不允许常量值作为合并语句的“源”。 【参考方案1】:

分享给所有还在使用德比的伤心人:) 所以我在merge into statement(https://db.apache.org/derby/docs/10.14/ref/rrefsqljmerge.html)的帮助下解决了这个问题:

MERGE INTO foo
USING SYSIBM.SYSDUMMY1
ON foo.id = '1' AND foo.language = 'en'
WHEN MATCHED THEN
  UPDATE SET name = 'name2', image = 'someImgUrl2'
WHEN NOT MATCHED THEN
  INSERT (id, name, language, image)
  VALUES ('1', 'name1', 'en', 'someImgUrl1')

其中 foo 是您要更新行的表,SYSIBM.SYSDUMMY1 derby 虚拟表只有 1 行无用(顺便说一句,它不适用于我的具有多行的常规表之一)

正如您可能理解的那样,它更像是解决方法,但总比没有达到 upsert 目标。

【讨论】:

拥有CREATE TABLE foo(id VARCHAR(2), name VARCHAR(48), language VARCHAR(48), image VARCHAR(48)) 并使用它我得到“java.sql.SQLSyntaxErrorException:列'FOO.ID'要么不在FROM列表中的任何表中,要么出现在连接规范中并且不在连接规范的范围或出现在 HAVING 子句中并且不在 GROUP BY 列表中。如果这是 CREATE 或 ALTER TABLE 语句,则 'FOO.ID' 不是目标表中的列。”。使用德比 10.14.2.0。 啊,找到问题了!您有“foo AS target”,但您仍然在其中使用“foo”。修复和支持。 另外,似乎在使用它时,插入部分中的列默认值不会像常规插入语句那样被尊重。这似乎是 Derby 中的一个错误。 感谢您的修复。我遇到的一个小故障是 Apache Derby 抛出“错误 42821:'BIGINT' 类型的列不能保存 'CHAR' 类型的值”如果输入的数字带有引号“1”而不是简单的 1。从数字中删除引号为我修好了。【参考方案2】:

以下内容适用于 Apache Derby 10.12.1.1:

merge into FOO
using RANDOM_TABLE
on FOO.guid = 'qwerty'
when matched then
    update set guid = 'qwerty'
when not matched then
    insert (guid) values('qwerty')       

这里 FOO 是我要执行 upsert 的目标表,而 ​​RANDOM_TABLE 是我数据库中的任何其他表。值“qwerty”是我的数据和唯一键。在这个例子中,FOO 只有一个列,但它应该可以分别向插入和更新添加更多列。

我觉得这种语法很不优雅,但至少它似乎避免了执行两个单独的语句来完成这项工作。

【讨论】:

奇怪,我刚收到Error code 30000, SQL state 42X01: Syntax error: Encountered "INTO" at line 7, column 12.

以上是关于我可以使用 MERGE INTO 在 Apache Derby 中模拟“upsert”吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何替换merge into

oracle merge into用法

数据库系列—— merge into用法

ORACLE11g update和merge into 的区别

oracle merge into 表里面的一部分数据怎样做?

oracle中多个进程可以使用merge into语句对同一个表操作不,能锁表吗