我听说过几次,由于性能原因,不应执行COUNT(*)
或SELECT *
,但无法找到更多信息。
我可以想象数据库正在使用所有列进行操作,这可能会导致令人印象深刻的性能损失,但我不确定。有人对这个主题有进一步的信息吗?
SQL 是一种声明式语言,你在其中指定 你想要什么。这与指定 如何 获得所需不同。这意味着数据库引擎可以自由地以它认为最高效的方式实现你的查询。许多数据库优化器会将你的查询重写为代价更小的替代方案(如果有这样的计划)。
假设有以下表:
table(
pk not null
,color not null
,nullable null
,unique(pk)
,index(color)
);
...所有以下内容在功能上是等价的(由于count和nulls的机制):
1) select count(*) from table;
2) select count(1) from table;
3) select count(pk) from table;
4) select count(color) from table;
无论使用哪种形式,如果查询优化器认为另一种形式更高效,它可以自由地将查询重写为另一种形式(但并非所有优化器都足够复杂以执行此操作)。唯一索引(pk)的占用空间比整个表要小,因此计算索引条目数量比扫描整个表更有效。在Oracle中,我们有位图索引,它还可以压缩重复字符串。如果我们在颜色列上使用了这样的索引,那么它可能是扫描最小的索引。Oracle还支持表压缩,在某些情况下,物理表比复合索引还要小。COUNT(*)
,因为它需要最少的认知努力来理解。
2. 在您编写并投入生产的代码中,SELECT *
的有效用途非常少。想象一张包含蓝光电影(是的,电影以 blob 的形式存储在此表中)的表。所以你将自己粘贴起来的神奇抽象层和SELECT * FROM movies where id = ?
放到了getMovies(movie_id)
方法中。我将克制自己不解释为什么SELECT name FROM movies
会在网络上传输得更快一些。当然,在大多数现实情况下,这不会有明显的影响。
关于性能的最后一点是,当查询中的所有引用列(被选择的、过滤的)都存在于索引中(称为覆盖索引)时,数据库根本不需要触及表。它可以完全从仅扫描索引来解析。通过选择所有列,您从优化器中删除了此选项。
另一个关于SELECT *
的事情比任何事情都更严重,那就是它会创建对表的特定物理布局的隐式依赖。让我解释一下。考虑以下表:table T1(name, id)
table T2(name, id)
以下语句将从数组arr中删除所有等于val的元素,并返回新数组的长度:insert into t1 select * from t2;
如果发生以下任何情况,... 将会中断或产生不同的结果:
2. 简短概述; 在可能的情况下,明确指定您想要的列(最终,您无论如何都必须这样做)。此外,选择较少的列比选择更多的列更快。显式选择的积极影响是它给予优化器更大的自由度。
select *
的例子,前提是这两个表因为其他原因需要格式相同。你的“破坏条件”应该是“T1获得一个非空列而T2没有”,以及“T2获得另一列而T1没有”。它将正确处理同时向两个表添加相同列的情况,而明确列出要使用的列的代码则会出错。 - supercatCOUNT(*)
与COUNT(column1)
是不同的!
COUNT(*)
返回记录数,不会使用更多资源,而COUNT(column1)
计算column1非空记录数。
对于SELECT
语句,情况不同。使用SELECT *
会请求更多数据。
count(*)
时,*
并不意味着“所有字段”。使用count(field)
将统计字段中所有非空值的数量,但是count(*)
将始终计算所有记录的数量,即使所有记录中的所有字段都为空,因此它根本不需要检查字段中的数据。select *
意味着您几乎总是返回比您要使用的更多的数据,这当然是一种浪费。然而,更为严重的是维护问题;如果您向表中添加字段,则查询也会返回这些字段。这可能意味着记录变得太大而无法适应缓冲区,导致错误消息。根据数据库的大小,它变得越来越低效。最简单的描述方式如下:
当您特定执行以下操作时:
SELECT column1,column2,column3 FROM table1
SELECT * FROM table1
MySQL 不知道你想要哪些列,它只知道你想要全部列但不知道列名,因此必须执行额外的任务来分析表以发现列,从而使用资源。
“*”代表“所有列”是绝对正确的。如果你有一张拥有大量列(比如100+)的表,这种查询在效率方面可能会很差。
我认为最好的解决方案是先创建数据库视图,预先过滤掉计数操作中涉及的记录数量,这样性能影响就不是一个大问题了,因为视图可以被缓存。
另一方面,似乎应该避免使用“*”运算符返回记录,最好选择你真正需要在业务中使用的字段。
使用 SELECT *
语句可能会影响性能。当应用程序实际上只需要少量列时,使用 SELECT *
语法的应用程序会在网络上传输比它们需要消耗的更多的数据,这是浪费的。
此外,在至少 Microsoft SQL Server 中,当您在视图中使用 SELECT *
,然后向基础表添加列时,视图返回的列标题和数据不匹配!有关此特定问题的详细信息,请参见 我的博客文章。
在使用COUNT(*)
时,情况取决于数据库及其版本。例如,在现代版本的MS SQL
中,它并不重要[需要来源]。
因此,对于COUNT(*)
,最好的方法是测量它。
使用SELECT *
是一个非常糟糕的想法。*
意味着读取所有列,这可能会产生沉重的IO和网络操作(特别是对于各种类型的CHAR
列)。此外--你很少需要所有的列。
COUNT(*)
的操作,因为它们会忽略列中的实际值。 - GabeSELECT *
。很多时候,我必须弄清楚为什么我的代码出错了,原因是数据库现在返回了一个新列。 - Neil Knight