使用SELECT *存在性能问题?

8

1
这可能是“最频繁的SO问题”的资格。:>D - dkretz
19个回答

11
如果你只需要某些列,那么你会给优化器带来麻烦(无法为索引选择或仅使用索引...)。
一些数据库可以选择仅从索引中检索数据。这非常有用并且可以大大加快速度。运行SELECT *查询不允许使用此技巧。
无论如何,从应用程序的角度来看,这不是一个好的实践。
以下是一个例子:
- 你有一个包含20个列(C1,C2,...,C19,C20)的表T。 - 你在T上有一个(C1,C2)的索引。 - 你执行SELECT C1,C2 FROM T WHERE C1=123 - 优化器具有所有索引信息,不需要访问表数据。 - 相反,如果你执行SELECT * FROM T WHERE C1=123,优化器需要获取所有列的数据,然后不能使用(C1,C2)上的索引。
在多个表连接时它非常有用。

我曾认为索引仅在JOIN、WHERE和GROUP BY子句中才相关。如果我错了,有人可以纠正我,但是选择子句中的列如何防止优化器选择索引呢? - Juliet
@Princess 我已经更新了帖子,并附上了一个例子。 - FerranB

11

那篇文章更多关注可维护性而非性能。我同意该帖子的答案,即 select * 是一种反模式,但这个问题是关于性能以及是否有差异的。 - Andrew Hare

5

唯一的性能问题是,如果您的应用程序只需要由select *返回的字段子集。在数据库中,它们实际上是相同的东西,因此没有性能差异。


2
+1 - 在回答这个问题时,这一点经常被忽视。如果只有三列命名为col1、col2和col3,则SELECT col1、col2、col3SELECT *是相同的。 - Fenton

5

每次使用 select * 时,可能需要额外查询列的列表。在高并发环境下,这可能会成为可见的开销,但偶尔使用一次不会有任何影响。

此外,在插入记录时,永远不要在插入语句中使用 select *,以防添加列。


我只是想问一下 - 当你在SELECT语句中明确指定表中的字段时,服务器会检查该字段是否真实存在,所以还会有额外的查询吗?或者我理解错了吗? - empi
这并不完全准确(至少对于某些数据库而言),大多数顶级数据库会为查询准备一个计划并缓存它,因此无论您使用*还是列列表,列的列表仍将在计划编译时查询。当表上发生DDL更改时,查询缓存将失效。 - Pop Catalin

2

我不了解计算性能,但在可读性/可维护性方面(即人的表现),我们在我的店里不使用select *。一切都是显式选择的。


2
我不是DBA,但根据我从我们的DBA学到的知识,推理(至少对于SQL Server而言)是因为DB缓存算法不能很好地缓存'*'查询,但如果您多次使用完全指定列运行相同的查询,则会很好地缓存它。
我相信更有经验的DBA可以详细介绍缓存机制的确切细节,但这就是性能下降的原因。
注意:只有在多次运行查询,特别是在短时间内运行查询时,缓存性能才有效,否则您将看不到性能差异。

2
也许吧。这很大程度上取决于数据库引擎,它如何存储数据,有多少行返回,还有其他列的大小等因素。
如果您正在使用基于行的数据库(即大多数数据库),它将所有列一起存储(除了BLOB通常单独存储,特别是较大的BLOB),那么执行SELECT *对服务器本身几乎没有影响- 它必须先获取整个行。
另一方面,如果您要通过网络发送数据(甚至在本地使用,因为它会影响所使用的缓冲区的大小等),那么拥有较少的列可能会有所帮助,因为要发送回的字节较少。但是,如果查询在任何方面都很困难(例如需要IO),则此差异可能会被服务器性能所掩盖。
如果行中有大型BLOB,则SELECT *不是很聪明-否则,它不太可能产生太大的差异,但也可能会产生差异。
还有一些“基于列”的数据库引擎-它们完全不同-对于它们,“SELECT *”是完全的性能杀手;请务必避免使用。如果您正在使用其中之一,则完全意识到这一点(通常它们用于非常大的数据仓库应用程序)。
对我来说,不使用“SELECT *”的主要优点是可维护性。当有人向表中添加额外列时,您不会感到惊讶;当某个您正在使用的列被删除时,您的查询将“快速失败”。它使代码更具自我记录性,因为某人可以随意查看您想要的列。

1
如果指定了所有字段,则在性能方面不应该有任何显着差异。但是,如果您只想从具有十几个列的表中选择几个特定字段,则速度会变慢。
使用SELECT *存在可读性和可维护性问题。始终使用特定的字段名称是有意义的,即使您想选择所有字段也是如此。

1

性能方面,不是很好。在一个表格中,比如有10列,连接了其他两个或更多的表格,特别是在结果集较大时,SELECT * 可以返回数十列数据,其中大部分甚至是未使用或无用的数据。从DBMS的负担来看,影响不大,但所有这些数据仍然需要以某种方式传输到网络上;网络带宽和延迟肯定会累加。我在高负载环境下亲眼见过这种情况。这绝对很重要。

除了带宽问题外,您还可能遇到模糊的列命名问题(消除歧义通常意味着从一开始就删除 SELECT *,所以最好从一开始就这样做),并且在代码内明确代码需求也被认为是良好的实践;这样做在许多方面都有帮助——调试、协作等。


1
如果在join查询中使用select *,则会自动发送比所需更多的信息,因为join字段会重复。这是一种浪费处理时间和网络资源的行为,并且可能会导致性能问题。此外,不指定字段意味着当添加新字段时,您的应用程序可能会出现问题,特别是如果它们是用户不打算看到但存在于审核或数据库类型处理中的字段。在insert中使用select *总是一个糟糕的想法,因为沿着某个地方的行路线,某些不太聪明的人可能会实际改变表中列的顺序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接