【问题标题】:Why is "while (rs.next())" necessary here?为什么这里需要“while (rs.next())”?
【发布时间】:2019-01-16 05:12:46
【问题描述】:

我想从我的数据库“Logs”中选择最大行号并将其存储在变量m中。

这是我的代码:

ResultSet rs = stmt.executeQuery("Select max(Line) as L from logs");

while (rs.next()) {          // Why do I need this
    int m = rs.getInt("L");
    System.out.println(m);
}

但除非我使用while(rs.next()),否则它不起作用。


如果我理解正确,rs.next() 会将光标移动到下一行,但在这里,在这个结果中,我只有 一行

那么,有人可以解释为什么循环是必要的吗?我唯一能想到的是第一个光标设置在列名上,对吗?

【问题讨论】:

  • 你可以只调用一次,但你想用尽结果集以防万一
  • 如果您只期望一行,请将其更改为 if (rs.next())
  • @shmosel 或rs.next(); int m = rs.getInt("L");
  • 我要编码if (!rs.next()) throw new Exception("something is really out of shape here")

标签: java jdbc


【解决方案1】:

有不同类型的执行命令。 游标用于从您执行的查询中读取数据。当您执行读取时,默认情况下您使用 Forward Only Cursor 因此您只有在调用 Recorset.Next() 后才能获得下一个结果!我不想在这里更深入。您可以在此处阅读有关游标的信息:https://docs.microsoft.com/en-us/sql/ado/guide/data/types-of-cursors-ado?view=sql-server-2017

在您的情况下,最好的解决方案是使用标量结果集,它只会返回 ONE CELL,这正是您想要实现的,而无需遍历您的结果集。以下示例显示了如何实现这样的:

using System;
using System.Data;
using System.Data.SqlClient;

class ExecuteScalar
{
  public static void Main()
  {
    SqlConnection mySqlConnection =new SqlConnection("server=(local)\\SQLEXPRESS;database=MyDatabase;Integrated Security=SSPI;");
    SqlCommand mySqlCommand = mySqlConnection.CreateCommand();
    mySqlCommand.CommandText ="SELECT COUNT(*) FROM Employee";
    mySqlConnection.Open();

    int returnValue = (int) mySqlCommand.ExecuteScalar();
    Console.WriteLine("mySqlCommand.ExecuteScalar() = " + returnValue);

    mySqlConnection.Close();
  }
}

我们使用 ExecuteScalar 只返回一个单元格。请记住,即使您的查询返回多个行/列,也只会返回 VERY FIRST CELL

【讨论】:

  • 这似乎是 C# 代码,但问题标记为 Java。
【解决方案2】:

ResultSet 游标最初位于第一行之前,即 第一次调用 next 方法使第一行成为当前行, 第二次调用使第二行成为当前行,依此类推。

考虑一下你有这样的东西。

->1->2->3
^

您的“rs”最初指向 1 之前,当您调用 rs.next() 时,它会前进箭头以指向 1

  ->1->2->3
    ^

所以如果你不调用 next() 方法,那么你不会得到结果。

希望对你有帮助

【讨论】:

    【解决方案3】:

    为什么?

    光标最初位于之前第一个元素。您需要提前一次才能访问第一个元素。

    这显然是因为使用循环遍历结果非常方便,如您所见。来自official documentation

    将光标从当前位置向前移动一行。 ResultSet 光标最初位于第一行之前;第一次调用 next 方法使第一行成为当前行;第二次调用使第二行成为当前行,依此类推。


    解决方案

    因此,虽然您不需要任何循环,但您需要将光标前进一次。从技术上讲,单个rs.next(); 就足够了:

    rs.next();
    // Access the result
    

    但是,您可能需要考虑根本没有匹配的情况,因为:

    当调用next 方法返回false 时,光标定位在最后一行之后。 任何需要当前行的ResultSet 方法的调用都将导致SQLException 被抛出。

    所以在这种情况下代码会失败。

    因此,您应该考虑该案例并使用返回的boolean 来保护您的访问权限:

    if (rs.next()) {
        // Access result ...
    } else {
        // No match ...
    }
    

    【讨论】:

    • 没有group byselect max(...) ... 语句将始终只返回一行,即使没有记录与where 子句匹配,在这种情况下返回值为null。所以,一个简单的rs.next(); 对这个 SQL 来说是完全正确的。
    • 为了清楚起见,我会使用if(rs.first()){ ... }。它会做完全相同的事情,但阅读起来会更清晰。
    • @Andreas 我会继续检查,所以我的 Java 的正确性不依赖于不同语言规定的规则。但我想这是个人喜好。实际上,如果分支确实引起了麻烦,我可能考虑在性能关键函数中跳过它,但只有在它上面的注释表明假设是什么。至少在下次有人更改 SQL 并且不考虑重新访问周围 Java 代码的逻辑时,这可以起到保护作用。
    • 但不在这里;-)
    【解决方案4】:

    来自the official documentation(顺便说一句,你应该读过):

    最初,光标位于第一行之前。 next 方法将光标移动到下一行,并且由于在 ResultSet 对象中没有更多行时返回 false,因此可以在 while 循环中使用它来遍历结果集。

    你基本上需要移动它,在你的情况下移动它一次就足够了:

     rs.next(); 
     System.out.println(rs.getInt("L")); 
    

    【讨论】:

      【解决方案5】:

      您可以将其转换为使用简单的if 语句。

      if(rs.next()) {
          // other code here
      }
      

      当你有不止一行要带回来时,你会使用while

      【讨论】:

      • 不需要 if 条件只需 rs.next(); int m = rs.getInt("L"); 因为 max 总是返回结果
      • @YCF_L 这取决于确切的查询:考虑一个空表,没有group bymax 将返回带有null 的单行,但使用group by 它将不返回任何行
      • @MoritzBoth 当然,对于 YCF_L 问题中的查询是绝对正确的,但鉴于问题 - 很多时候 - 简化示例,您也应该考虑更大的图景。在这种情况下,我的回复是由 YCF_L 最初评论中的 always 触发的。
      猜你喜欢
      • 2012-06-03
      • 1970-01-01
      • 2021-08-27
      • 2010-12-15
      • 2021-10-22
      • 2012-09-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多