Statement.executeUpdate()返回-1时的含义是什么?
在management studio和executeUpdate
中运行的查询使得相同的executeUpdate
返回-1
,这在我们可以找到的任何文档中都是未定义的。 它应该只返回rowcount或0
。 这是什么意思? 如果重要,驱动程序是JDBC-ODBC桥。
例:
String query = "IF NOT EXISTS (SELECT * FROM animals WHERE animal_name ='" + a +"') INSERT INTO " + table + " (animal_name, animal_desc, species_id) VALUES ('" + a + "', '" + b + "', " + c + ")"; int result = statement.executeUpdate(query); System.out.println(result);
查询有效,因为该行被添加到数据库中,奇怪的是它返回-1,文档说它只返回0或rowcount(因为我已经更正)。
更新:
在Management Studio中运行此结果,并且“命令已成功完成”。
IF NOT EXISTS (SELECT * FROM animals WHERE animal_name = 'a') INSERT INTO animals(animal_name, animal_desc, species_id) VALUES ('a', 'a', 1)
这应该意味着该方法应返回0,因为它不返回任何内容,对吗?
由于执行的语句实际上不是DML(例如UPDATE
, INSERT
或EXECUTE
),而是一段包含 DML的T-SQL,我怀疑它不被视为更新查询。
JDBC 4.1规范的第13.1.2.3节陈述了一些内容(很难解释为btw):
当方法
execute
返回true时,调用方法getResultSet
以检索ResultSet对象。 当execute
返回false时,方法getUpdateCount
返回一个int。 如果此数字大于或等于零,则表示该语句返回的更新计数。 如果为-1,则表示没有更多结果。
鉴于此信息,我想executeUpdate()
内部execute()
,然后 – 因为execute()
将返回false
– 它将返回getUpdateCount()
的值,在这种情况下 – 根据JDBC规范 -将返回-1
。
这一点得到以下事实的进一步证实:1) Statement.executeUpdate()
的Javadoc说:
返回:(1)SQL数据操作语言(DML)语句的行计数或(2)0表示不返回任何内容的SQL语句
2) Statement.getUpdateCount()的Javadoc指定:
当前结果作为更新计数; -1如果当前结果是ResultSet对象或没有更多结果
只是为了澄清一下:给定了用于executeUpdate()
的Javadoc,行为可能是错误的,但可以解释。
另外,正如我在其他地方评论的那样,-1可能只是表示:可能有些东西被改变了,但我们根本不知道,或者我们无法给出准确数量的变化(例如,因为在这个例子中它是一块T-执行的SQL)。
4年后, 微软在Github上开源了他们的JDBC驱动程序 。 今天我收到了关于这个问题的通知,然后去看看,我相信我已经找到了罪魁祸首, mssql-jdbc/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java:1713
。
基本上,如果SQL Server不是一个确定的结果集,它会尝试理解SQL Server发回的内容。 根据评论,它是这样的:
首先检查错误。 (1669年)
不是错误。 它是结果集吗? (1680年)
不是错误或结果集。 也许是T-SQL语句的结果? 也就是说,以下之一:
- 受影响的行数的正数(来自INSERT,UPDATE或DELETE),
- 零表示没有受影响的行,或者语句是DDL,或
- a -1表示语句成功,但没有可用的更新计数信息 (转换为批量更新计数数组中的Statement.SUCCESS_NO_INFO)。 (ln 1706)
以上都不是。 最后一次机会……进入上面的解析器,我们知道更多结果最初是真的。 如果我们发出moreResults false,我们点击DONE标记(DONE(FINAL)或DONE(批处理)),表示批处理总体成功,但没有关于单个语句更新计数的信息。 这与上面的最后一种情况类似,只是没有更新计数。 那就是:我们有一个成功的结果(返回true),但我们没有关于它的其他信息(updateCount = -1)。 (1693年)
到达这里的唯一方法(moreResults仍然是真的,但没有任何明显的结果)是TDSParser实际上没有解析任何东西 。 也就是说,我们在响应中处于EOF状态。 在那种情况下,确实没有更多的结果。 我们完成了。 (1717年)
(强调我的)
所以你们最后都是对的。 SQL根本无法判断受影响的行数,默认为-1
。 🙂
我也没有在任何地方看到这一点,但我的直觉是,这意味着IF
阻止了整个语句的执行。
尝试使用IF
传递的数据库运行该语句。
还要检查是否涉及可能改变结果的任何触发器。
[编辑]当标准声明此函数永远不会返回-1
,这不会强制执行此操作。 Java没有前置条件和后置条件。 JDBC驱动程序可以返回一个随机数,但无法阻止它。
如果知道发生这种情况的原因很重要,请对不同的数据库运行语句,直到您尝试了所有执行路径(即IF
返回false
和返回true
)。
如果它不那么重要,请将其标记为微软工程师的“聪明伎俩”,并记住,当您下次想要自己聪明时,您有多喜欢它。
对于针对DB2 for z / OS服务器的executeUpdate语句,返回的值取决于正在执行的SQL语句的类型:
对于可以具有更新计数的SQL语句(例如INSERT,UPDATE或DELETE语句),返回的值是受影响的行数。 有可能:
如果正行数受操作影响,则为正数,并且操作不是分段表空间上的批量删除。
0,如果没有行受操作影响。
-1,如果操作是分段表空间上的批量删除。
对于DB2 CALL语句, 返回值-1,因为DB2数据库服务器无法确定受影响的行数。 对CALL语句的getUpdateCount或getMoreResults的调用也返回-1。 对于任何其他SQL语句,返回值-1。
这并不能解释为什么会这样,但它解释了为什么会发生这种情况。 以下字节代码将-1
设置为SQLServerStatement
构造函数中的内部updateCount
标志:
// Method descriptor #401 (Lcom/microsoft/sqlserver/jdbc/SQLServerConnection;II)V // Stack: 5, Locals: 8 SQLServerStatement( com.microsoft.sqlserver.jdbc.SQLServerConnection arg0, int arg1, int arg2) throws com.microsoft.sqlserver.jdbc.SQLServerException; // [...] 34 aload_0 [this] 35 iconst_m1 36 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27]
现在,我不会分析所有可能的控制流,但我只是说这是内部默认初始化值,它以某种方式泄露给客户端代码。 注意,这也是在其他方法中完成的:
// Method descriptor #383 ()V // Stack: 2, Locals: 1 final void resetForReexecute() throws com.microsoft.sqlserver.jdbc.SQLServerException; // [...] 10 aload_0 [this] 11 iconst_m1 12 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27] // Method descriptor #383 ()V // Stack: 3, Locals: 3 final void clearLastResult(); 0 aload_0 [this] 1 iconst_m1 2 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27]
换句话说,你可能安全地将-1
解释为与0
相同。 如果您依赖此结果值,可以保持安全并按以下方式进行检查:
// No rows affected if (stmt.executeUpdate() <= 0) { } // Rows affected else { }
更新 :在阅读Mark Rotteveel的回答时 ,我倾向于同意他,假设-1
是“未知更新计数”的符合JDBC的值。 即使没有在相关方法的Javadoc中记录这一点,它也会在JDBC规范中记录,第13.1.2.3节返回未知或多个结果 。 在这种情况下,可以说IF .. INSERT ..
语句将具有“未知更新计数” ,因为此语句无论如何都不符合SQL标准。