29

一个在管理工作室中工作的查询和在executeUpdate相同的executeUpdatereturn中工作-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 或行数(正如我已经更正的那样)。

更新:

在 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 因为它不返回任何内容,对吗?

4

5 回答 5

27

由于执行的语句实际上不是 DML(例如UPDATEINSERTEXECUTE),而是一段包含DML 的 T-SQL,我怀疑它不被视为更新查询。

JDBC 4.1 规范的第 13.1.2.3 节说明了一些事情(顺便说一句很难解释):

当该方法execute返回 true 时,getResultSet调用该方法以检索 ResultSet 对象。当execute返回 false 时,该方法 getUpdateCount返回一个 int。如果这个数字大于或等于零,则表示语句返回的更新计数。如果为-1,则表示没有更多结果。

鉴于此信息,我猜想在executeUpdate()内部执行一个execute(),然后 -execute()将返回false- 它将返回 的值getUpdateCount(),在这种情况下 - 根据 JDBC 规范 - 将返回-1

事实 1) 进一步证实了这一点,Javadoc forStatement.executeUpdate()说:

返回:(1) SQL 数据操作语言 (DML) 语句的行数或 (2) 0 用于不返回任何内容的 SQL 语句

并且 2) Statement.getUpdateCount()的 Javadoc指定:

当前结果作为更新计数;-1 如果当前结果是 ResultSet 对象或没有更多结果

只是为了澄清:鉴于executeUpdate()行为的 Javadoc 可能是错误的,但可以解释。

同样,正如我在其他地方评论的那样,-1 可能只是表示:也许有些东西发生了变化,但我们根本不知道,或者我们无法给出准确的变化数量(例如,因为在这个例子中它是一块 T-执行的 SQL)。

于 2012-09-13T17:57:32.183 回答
10

所以 4 年后,微软在 Github 上开源了他们的 JDBC 驱动程序。今天收到了关于这个问题的通知,去看看,相信我在这里找到了罪魁祸首,mssql-jdbc/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java:1713

基本上,驱动程序会尝试了解 SQL Server 发回的内容(如果不是明确的结果集)。根据评论,它是这样的:

  1. 首先检查错误。(1669 年)

  2. 不是错误。是结果集吗?(1680 年)

  3. 不是错误或结果集。也许是 T-SQL 语句的结果?即,以下之一:

    • 受影响的行数的正计数(来自 INSERT、UPDATE 或 DELETE),
    • 零表示没有行受到影响,或者语句是 DDL,或者
    • -1 表示语句成功,但没有可用的更新计数信息(转换为批更新计数数组中的 Statement.SUCCESS_NO_INFO)。(1706 年)
  4. 以上都不是。最后一次机会...进入上面的解析器,我们知道 moreResults 最初是正确的。如果我们的 moreResults 为 false,我们会点击一个 DONE 标记(DONE(FINAL)或 DONE(批处理中的 RPC)),表明批处理总体成功,但没有关于单个语句的更新计数的信息。这类似于上面的最后一种情况,只是没有更新计数。即:我们有一个成功的结果(返回 true),但我们没有关于它的其他信息(updateCount = -1)。(1693 年)

  5. 到达这里的唯一方法(moreResults 仍然正确,但没有任何明显的结果)是如果 TDSParser 实际上没有解析 任何东西。也就是说,我们在响应中处于 EOF。在那种情况下,真的没有更多的结果。我们完成了。(1717 年)

(强调我的)

所以你们最后是对的。SQL 根本无法判断有多少行受到影响,默认为-1. :)

于 2016-11-21T08:58:37.427 回答
5

我也没有在任何地方看到过这种情况,但我的直觉是这意味着IF阻止了整个语句的执行。

尝试使用通过的数据库运行该语句IF

还要检查是否有任何可能改变结果的触发器。

[编辑]标准说这个函数永远不应该返回-1时,这并没有强制执行。Java 没有前置条件和后置条件。JDBC 驱动程序可以返回一个随机数,并且无法阻止它。

如果知道为什么会发生这种情况很重要,请针对不同的数据库运行该语句,直到您尝试了所有执行路径(即IF返回的路径false和返回的路径true)。

如果它不是那么重要,请将其标记为 Microsoft 工程师的“聪明把戏”,并记住当您下次想自己聪明时有多喜欢它。

于 2012-09-13T07:15:06.230 回答
5

对于针对 DB2 for z/OS 服务器的 executeUpdate 语句,返回的值取决于正在执行的 SQL 语句的类型:

对于可以具有更新计数的 SQL 语句,例如 INSERT、UPDATE 或 DELETE 语句,返回值是受影响的行数。有可能:

一个正数,如果操作影响了正数的行,并且该操作不是对分段表空间的批量删除。

0,如果没有行受操作影响。

-1,如果操作是对分段表空间的批量删除。

对于 DB2 CALL 语句,返回值 -1,因为 DB2 数据库服务器无法确定受影响的行数。对 CALL 语句的 getUpdateCount 或 getMoreResults 的调用也返回 -1。对于任何其他 SQL 语句,返回值 -1。

于 2012-09-13T07:49:09.303 回答
4

这并不能解释为什么它应该是这样,但它解释了为什么它会发生。以下字节码设置-1为构造函数中的内部updateCount标志SQLServerStatement

// 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 章 Returning Unknown or Multiple Results中。在这种情况下,可以说IF .. INSERT ..语句将具有“未知更新计数”,因为该语句无论如何都不符合 SQL 标准。

于 2012-09-13T07:47:54.943 回答