52

使用 JDBC 时,我经常遇到类似的结构

ResultSet rs = ps.executeQuery();
while (rs.next()) {
    int id = rs.getInt(1);
    // Some other actions
}

我问自己(以及代码作者)为什么不使用标签来检索列值:

int id = rs.getInt("CUSTOMER_ID");

我听到的最好的解释是关于性能的。但实际上,它是否使处理速度非常快?我不相信,尽管我从未进行过测量。在我看来,即使按标签检索会慢一些,但它提供了更好的可读性和灵活性。
那么有人可以给我很好的解释来避免通过列索引而不是列标签来检索列值吗?两种方法的优缺点是什么(也许,关于某些 DBMS)?

4

13 回答 13

57

警告:我要在这里夸夸其谈,因为这让我发疯。

99%* 的时间里,人们有一些模糊的想法让事情变得“更好”是一种荒谬的微优化。这完全忽略了这样一个事实,除非您一直处于极其紧凑和繁忙的循环中,处理数百万个 SQL 结果(希望这种情况很少见),否则您永远不会注意到它。对于不这样做的每个人,开发人员维护、更新和修复列索引中的错误的时间成本远远大于性能极差的应用程序的硬件增量成本。

不要对这样的优化进行编码。为维护它的人编写代码。然后观察、测量、分析和优化。再观察,再测量,再分析,再优化。

优化几乎是开发的最后一步,而不是第一步。

* 图是组成的。

于 2008-10-09T11:26:27.550 回答
49

您应该默认使用字符串标签。

优点:

  • 列顺序的独立性
  • 更好的可读性/可维护性

缺点:

  • 您无法控制列名(通过存储过程访问)

你更喜欢哪个?

整数?

int i = 1;  
customerId = resultSet.getInt(i++);  
customerName = resultSet.getString(i++);  
customerAddress = resultSet.getString(i++);

还是字符串?

customerId = resultSet.getInt("customer_id");  
customerName = resultSet.getString("customer_name");  
customerAddress = resultSet.getString("customer_address");

如果在位置 1 插入了一个新列怎么办?你更喜欢哪个代码?或者,如果列的顺序发生了变化,您需要更改哪个代码版本?

这就是为什么您应该默认使用字符串标签的原因。

于 2008-10-09T14:07:06.493 回答
7

答案已被接受,不过,这里有一些我还没有看到的额外信息和个人经验。

如果可能,一般使用列名(最好使用常量而不是文字)。这既更清晰,更易于维护,而且未来的更改不太可能破坏代码。

但是,列索引也有用途。在某些情况下,这些速度更快,但不足以覆盖上述名称的原因*。这些在开发处理ResultSets 的工具和通用方法时非常有价值。最后,可能需要索引,因为列没有名称(例如未命名的聚合)或存在重复名称,因此没有简单的方法来引用两者。

*请注意,我编写了一些 JDBC 驱动程序并查看了一些开源驱动程序,并且在内部这些驱动程序使用列索引来引用结果列。在我使用过的所有情况下,内部驱动程序首先将列名映射到索引。因此,您可以很容易地看到,在所有这些情况下,列名总是需要更长的时间。不过,这可能不适用于所有驱动程序。

于 2010-02-04T05:10:51.520 回答
6

从java文档:

ResultSet 接口提供用于从当前行检索列值的 getter 方法(getBoolean、getLong 等)。可以使用列的索引号或列的名称来检索值。一般来说,使用列索引会更有效。列从 1 开始编号。为了获得最大的可移植性,每行中的结果集列应按从左到右的顺序读取,并且每列应仅读取一次。

当然,每种方法(命名或索引)都有它的位置。我同意命名列应该是默认值。但是,在需要大量循环并且 SELECT 语句在同一段代码(或类)中定义和维护的情况下,索引应该没问题 - 建议列出正在选择的列,而不仅仅是“SELECT * FROM...”,因为任何表更改都会破坏代码。

于 2013-06-26T06:27:26.633 回答
4

我在 Oracle 数据库上对这个确切的主题进行了一些性能分析。在我们的代码中,我们有一个包含许多列和大量行的 ResultSet。在 20 秒 (!) 中,请求执行方法 oracle.jdbc.driver.ScrollableResultSet.findColumn(String name) 大约需要 4 秒。

显然整体设计有问题,但是使用索引而不是列名可能会花费 4 秒的时间。

于 2008-12-31T09:06:00.647 回答
4

当然,使用列名可以提高可读性并使维护变得容易。但是使用列名有另一面。如您所知,SQL 允许多个同名的列名,不能保证您在 resultSet 的 getter 方法中键入的列名实际上指向您要访问的列名。理论上,最好使用索引号而不是列名,但这会降低可读性。

于 2010-02-04T04:57:55.133 回答
3

你可以两全其美!使用索引的速度与使用列名的可维护性和安全性。

首先 - 除非您通过结果集循环,否则只需使用列名。

  1. 定义一组整数变量,一个用于您将访问的每一列。变量的名称可以包括列的名称:例如 iLast_Name。

  2. 在结果集循环之前遍历列元数据并将每个整数变量的值设置为相应列名的列索引。如果“Last_Name”列的索引为 3,则将“iLast_Name”的值设置为 3。

  3. 在结果集循环中,在 GET/SET 方法中使用整数变量名称。变量名称是开发人员/维护人员关于正在访问的实际列名称的视觉线索,但值是列索引,将提供最佳性能。

注意:初始映射(即列名到索引的映射)仅在循环之前完成一次,而不是针对循环中的每个记录和列。

于 2010-11-28T21:00:09.327 回答
2

我认为使用标签不会对性能产生太大影响。但是还有另一个不使用Strings 的原因。或者ints,就此而言。

考虑使用常量。使用int常量使代码更具可读性,但也不太可能出现错误。

除了更具可读性之外,该常量还可以防止您在标签名称中打错字 - 如果您这样做,编译器会抛出错误。任何有价值的 IDE 都会选择它。如果您使用Strings 或,情况并非如此ints

于 2008-10-09T11:27:11.697 回答
1

JDBC 驱动程序负责对列进行索引查找。因此,如果您每次驱动程序进行查找(通常在哈希映射中)时按列名提取值以检查列名的相应索引。

于 2008-10-09T11:24:20.533 回答
0

我同意以前的答案,即性能并不是可以迫使我们选择任何一种方法的东西。最好考虑以下事项:

  • 代码可读性:对于每个阅读代码标签的开发人员来说,比索引更有意义。
  • 维护:考虑 SQL 查询及其维护方式。在修复/改进/重构 SQL 查询后,您的情况更有可能发生什么:更改提取列的顺序或更改结果列名称。在我看来,更改提取列的顺序(作为在结果集中添加/删除新列的结果)更有可能发生。
  • 封装:无论您选择哪种方式,都尝试隔离在同一组件中运行 SQL 查询和解析结果集的代码,并仅让该组件知道列名及其与索引的映射(如果您决定使用它们)。
于 2008-10-09T12:20:59.110 回答
0

使用索引是一种优化的尝试。

开发人员需要花费额外的精力来查找必要的数据以检查他们的代码在更改后是否可以正常工作,从而节省了由此节省的时间。

我认为使用数字而不是文本是我们的本能。

于 2008-10-09T12:29:17.687 回答
0

除了在 Map 中查找标签之外,它还会导致额外的字符串创建。虽然它会在堆栈上发生,但它仍然需要付出代价。

这完全取决于个人选择,到目前为止我只使用索引:-)

于 2008-12-19T09:20:11.600 回答
0

正如其他海报所指出的那样,我会坚持使用列名,除非你有一个非常强大的理由不这样做。与查询优化等相比,对性能的影响可以忽略不计。在这种情况下,维护比小的优化更重要。

于 2019-04-15T14:47:59.693 回答