我可以使用 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”吗?的主要内容,如果未能解决你的问题,请参考以下文章
ORACLE11g update和merge into 的区别