Answers:
如果SELECT COUNT(*) FROM TABLE
只返回一行(计数),则相对较轻,并且是获取该基准的方法。
这SELECT *
不是物理上的禁忌,因为这是合法且允许的。
但是,问题SELECT *
在于您可能导致更多的数据移动。您对表中的每一列进行操作。如果SELECT
仅包含几列,则可以从一个或多个索引中获得答案,这可以减少I / O以及对服务器缓存的影响。
因此,是的,建议您不要这样做,因为这会浪费您的资源。
唯一真正的好处SELECT *
是不必键入所有列名。但是从SSMS中,您可以使用拖放操作来获取查询中的列名,并删除不需要的列名。
打个比方:如果某人使用SELECT *
时不需要每一列,那么当他们不需要每一行时是否也会使用SELECT
而没有WHERE
(或其他限制子句)?
除了已经提供的答案之外,我觉得值得指出的是,开发人员在使用诸如实体框架之类的现代ORM时往往过于懒惰。尽管DBA尽力避免SELECT *
,但是开发人员经常在c#Linq中编写语义等效的代码:
var someVariable = db.MyTable.Where(entity => entity.FirstName == "User").ToList();
本质上,这将导致以下结果:
SELECT * FROM MyTable WHERE FirstName = 'User'
还存在尚未解决的额外开销。那就是将每一行中的每一列处理到相关对象所需的资源。此外,对于保存在内存中的每个对象,必须清除该对象。如果仅选择所需的列,则可以轻松节省超过100mb的内存。虽然它本身不是一个很大的数量,但是它的垃圾回收等累积作用是成本方面的。
所以是的,至少对我来说,这将永远是一个大问题。我们还需要教育这样做的“隐性”成本。
附录
以下是根据注释要求仅提取所需数据的示例:
var someVariable = db.MyTable.Where(entity => entity.FirstName == "User")
.Select(entity => new { entity.FirstName, entity.LastNight });
性能:带有SELECT *的查询可能永远不会是覆盖查询(简单说明,堆栈溢出说明)。
面向未来:您的查询今天可能返回所有七列,但是如果有人在明年添加五列,则一年后您的查询将返回十二列,这浪费了IO和CPU。
索引:如果希望视图和表值函数参与SQL Server中的索引,则必须使用架构绑定来创建这些视图和函数,这禁止使用SELECT *。
最佳实践:切勿SELECT *
在生产代码中使用。
对于子查询,我更喜欢WHERE EXISTS ( SELECT 1 FROM … )
。
编辑:要解决Craig Young在下面的评论,在子查询中使用“ SELECT 1”不是“优化”-是的,因此我可以站在我的课前说“不要使用SELECT *,无例外!” ”
关于我可以想到的唯一例外是,客户端正在执行某种数据透视表操作,并且确实需要所有现在和将来的列。
尽管我想查看执行计划,但我可能会接受涉及CTE和派生表的异常。
请注意,我认为COUNT(*)
这是一个例外,因为它是“ *”的不同语法用法。
在SQL Server 2012(或2005年以后的任何版本)中,使用SELECT *...
仅是查询的顶级SELECT语句中可能的性能问题。
因此,在Views(*),子查询,EXIST子句,CTE或其他SELECT COUNT(*)..
等等中,这都不是问题。请注意,这对于Oracle,DB2和PostGres 也可能适用(不确定) ,但是很可能在许多情况下,对于MySql来说,这仍然是一个问题。
要了解原因(以及为什么它仍然是顶级SELECT中的问题),有助于理解为什么它曾经是一个问题是有帮助的,这是因为使用SELECT *..
意味着“ 返回所有列 ”。一般来说,这将返回大量的数据比你真正想要的,这显然会导致其它更多的IO,磁盘和网络。
不太明显的是,这也限制了SQL优化器可以使用的索引和查询计划,因为它知道最终必须返回所有数据列。如果它可以提前知道只需要某些列,那么它通常可以利用只有那些列的索引来使用更有效的查询计划。幸运的是,有一种方法可以让它提前知道这一点,即可以在列列表中显式指定所需的列。但是,当您使用“ *”时,您就放弃了,而赞成“只要给我所有东西,我就会弄清楚我需要什么”。
是的,处理每一列还需要额外的CPU和内存使用量,但是与这两件事相比,它几乎总是很小的:不需要的列需要大量的额外磁盘和网络带宽,并且必须使用更少的资源优化的查询计划,因为它必须包含每列。
那么,什么改变了?基本上,SQL Optimizers成功地合并了一个称为“列优化”的功能,该功能仅表示,如果您要在查询的较高级别中实际使用列,则它们现在可以在较低级别的子查询中找到。
这样做的结果是,如果在查询的较低/较高级别中使用“ SELECT * ..”,则不再重要。相反,真正重要的是顶级SELECT的列列表中的内容。除非您SELECT *..
在顶部使用,否则必须再次假定您需要所有列,因此不能有效地使用列优化。
(*-注意,在View中存在一个不同的较小的绑定问题,*
即使用“ *”时它们并不总是在列列表中注册更改。还有其他方法可以解决此问题,并且这不会影响性能。)
物理上和问题上都允许使用select * from table
它,但是,这是一个坏主意。为什么?
首先,您会发现您正在返回不需要的列(资源繁重)。
其次,与命名列相比,在大表上花费的时间更长,因为当您选择*时,实际上是从数据库中选择列名称,然后说“给我与其他列表中具有名称的列关联的数据。” 尽管这对于程序员来说是快速的,但请想象一下,在银行的计算机上进行查找,该计算机在一分钟内可能实际上有成千上万次查找。
第三,这样做实际上会使开发人员更难。您需要多久从SSMS来回切换到VS才能获得所有列名?
第四,这是懒惰编程的标志,我认为没有任何开发人员会想要这种声誉。
如果将Select * ...
代码放在程序中,可能会出现问题,因为如前所述,数据库可能会随时间变化,并且列的数量比编写查询时的预期要多。这可能会导致程序失败(最佳情况),或者程序可能会继续前进并破坏某些数据,因为它正在查看未编写为处理的字段值。简而言之,生产代码应始终在中指定要返回的字段SELECT
。
话虽如此,当Select *
成为EXISTS
子句的一部分时,我的问题就更少了,因为要返回给程序的所有内容都是一个布尔值,指示选择的成功与否。其他人可能不同意这种立场,我尊重他们对此的看法。编码的效率可能Select *
比在EXISTS
子句中编码“选择1”的效率低一些,但我认为,无论哪种方式,都不会存在数据损坏的危险。
很多答案为什么select *
错了,所以我会在我认为正确或至少可以的时候进行介绍。
1)在EXISTS中,查询的SELECT部分的内容将被忽略,因此您甚至可以编写SELECT 1/0
,并且不会出错。EXISTS
只是验证某些数据将返回,并根据此返回布尔值。
IF EXISTS(
SELECT * FROM Table WHERE X=@Y
)
2)这可能会select *
引发火灾,但是我喜欢在历史记录表触发器中使用。通过select *
,它防止主表获取新列而不将其添加到历史表中,并且在将主表插入/更新/删除时会立即出错。这避免了开发人员多次添加列而忘记将其添加到历史表的情况。
SELECT 1
因为它最明显地将您的意图通知未来的代码维护者。这不是必需的,但是如果我看到... WHERE EXISTS (SELECT 1 ...)
它很明显就宣布自己是一个真相测试。
SELECT 1
基于一个神话,那就是性能会好于SELECT *
。但是,两种选择都是完全可以接受的。由于优化器处理EXISTS的方式,性能没有差异。在可读性上也没有任何区别,因为“ EXISTS”一词清楚地表明了事实测试。
Column8
在主表中添加了忘记历史记录表的内容。开发人员将一堆代码写到列8中。然后,他将添加Column9
到主表中。这次记得也增加了历史。后来在测试时,他意识到他忘记添加Column9
历史记录(由于您的错误检测技术所致),因此立即添加了它。现在触发器似乎起作用了,但是历史记录中混入了第8列和第9列中的数据。:S