哪个更快/最好?SELECT *或SELECT column1,colum2,column3等


166

我听说SELECT *在编写SQL命令时通常不宜使用这种做法,因为它对于SELECT您特别需要的列更有效。

如果我需要SELECT表中的每一列,我应该使用

SELECT * FROM TABLE

要么

SELECT column1, colum2, column3, etc. FROM TABLE

在这种情况下,效率真的重要吗?我认为SELECT *,如果您确实需要所有数据,则内部会更理想,但我是在没有真正理解数据库的情况下说这一点。

我很好奇这种情况下的最佳做法。

更新:我可能应该指定,我真正想要做的唯一情况SELECT *是当我从一个表中选择数据时,我知道所有列都将始终需要检索,即使添加了新列也是如此。

但是,鉴于我所看到的答复,这似乎仍然是一个坏主意,SELECT *绝不应该出于我曾经遇到的更多技术原因而使用它。




1
是的,它是其中大多数的重复。
乔治·斯托克2010年

Answers:


168

选择特定列更好的原因之一是,它增加了SQL Server可以从索引访问数据而不是查询表数据的可能性。

这是我写的一篇文章: 真正的原因是选择查询的索引覆盖率很差

更改也不那么容易,因为任何消耗数据的代码都将获得相同的数据结构,而不管您将来对表模式所做的更改如何。


3
为此+1。如果所有引用的列都存在于一个索引(“覆盖索引”)中,则说明您感到震惊。
伊恩·尼尔森

22
那不是他的问题的答案-“如果我需要选择表中的每一列,...” –在这种情况下,* vs col1,.. coln无关紧要(但这对于程序员来说确实有用,因为*较短!)。
Matt Rogish

3
仍然很重要,因为选择列表是一种合同形式,尤其是如果SQL在存储过程中。
Eric Z Beard

4
虽然乔恩所说的是完全正确的,也是非常正确的观点,但我必须同意,所问的问题是关于它们是否已经要求所有列了。由于这部分问题,真正的问题是面对架构更改时的脆弱性。
IDisposable

1
@MattRogish先生,您正确地理解了这两种方法(* vsall_column_names)之间是否存在性能差异,而我们却拥有成千上万的行,并且使用索引(在WHERE子句中)执行SELECT?
santosh

59

鉴于你的规范,你选择所有列,几乎没有什么差别 此时。但是请意识到,数据库架构确实会发生变化。如果您使用了SELECT *代码,那么即使有很大的可能,您的代码也没有准备好使用或显示该新数据,但是您将要在表中添加任何新列。这意味着您要将系统暴露于意外的性能和功能更改。

您可能愿意以较小的费用来解决此问题,但是您意识到不需要的列仍然必须是:

  1. 从数据库读取
  2. 通过网络发送
  3. 编组到您的流程中
  4. (对于ADO类型的技术)保存在内存中的数据表中
  5. 忽略并丢弃/垃圾收集

项#1具有许多隐藏成本,包括消除一些潜在的覆盖索引,导致数据页负载(以及服务器缓存抖动),发生行/页/表锁定,而这些锁定本来可以避免。

将这与指定an的潜在节省(相对于an)进行平衡*,唯一的潜在节省是:

  1. 程序员无需重新访问SQL即可添加列
  2. SQL的网络传输更小/更快
  3. SQL Server查询解析/验证时间
  4. SQL Server查询计划缓存

对于第1项,实际情况是您将添加/更改代码以使用无论如何都可能会添加的任何新列,所以这很容易。

对于第2项,差异很少会迫使您进入不同的数据包大小或数量的网络数据包。如果到了SQL语句传输时间成为主要问题的地步,则可能需要首先降低语句的速率。

对于第3项,由于必须进行扩展,因此没有节省*,这意味着无论如何都要查询表模式。实际上,列出列会产生相同的成本,因为必须根据架构进行验证。换句话说,这是彻底的清洗。

对于第4项,当您指定特定列,查询计划缓存可以得到更大的,但只有当你正在处理不同的列集合(这是不是您所指定的)。在这种情况下,您确实需要不同的缓存条目,因为您需要根据需要使用不同的计划。

因此,由于您指定问题的方式,面对最终的模式修改,所有这些归结为问题的弹性。如果将这种模式刻录到ROM中(发生),则*完全可以接受。

但是,我的一般指导原则是只应选择所需的列,这意味着有时看起来像您要所有这些列,但是DBA和模式演变意味着可能会出现一些新列,这些列可能会极大地影响查询。 。

我的建议是,您应该始终选择特定的列。请记住,您一遍又一遍地会做得很好,所以要养成正确做事的习惯。

如果您想知道为什么不更改代码就可以更改模式,请考虑审计日志,有效/有效日期以及DBA为系统性地解决合规性问题而添加的其他类似内容。不当更改的另一个来源是系统或用户定义字段中其他地方的性能反规范化。


3
“现实是,您将添加/更改代码以使用无论如何都可能会添加的任何新列,因此这很容易。” -仅当您在代码中按名称手动读取每一列时。如果您使用的是自动映射,则不是这种情况,这个问题变得很重要。
乔什·诺

36

您只应选择所需的列。即使您需要所有列,也最好列出列名,以便sql server不必在系统表中查询列。

另外,如果有人向表中添加列,则您的应用程序可能会中断。您的程序将获得未曾期望的列,并且可能不知道如何处理它们。

除此之外,如果表具有二进制列,则查询将更慢并且将使用更多的网络资源。


6
如此一来,通过使用*,您将为数据库添加额外的工作。好的,这是我没想到的原因之一。
Ankur 2010年

1
+1可能会提早打破/捕获错误。我认为关于效率的讨论是有效的,但YAGNI。
nailitdown

6
SQL Server是否仍然需要验证或检查“ col1”是否在指定的表中,即查询系统表?
Patrick

3
对性能的最大影响可能与索引有关。如果您要查找的列是用于查找数据的索引的一部分,则服务器将在那里直接获取数据,如果您执行select *,则很有可能必须执行称为书签查找的操作,这需要额外的操作扫描以查找您可能甚至不需要的其余基础数据。
Cobusve 2010年

3
@Patrick-当场。有许多避免*的充分理由,但这不是其中之一。
Martin Smith 2010年

31

select *坏事有四个大原因:

  1. 最重要的实际原因是,它迫使用户神奇地知道将返回列的顺序。最好是明确的,这也可以保护您免受表更改的影响,因为表更改可以很好地与...

  2. 如果您正在使用的列名称发生更改,则最好尽早(在SQL调用时)捕获它,而不是尝试使用不再存在(或名称已更改等)的列。 )

  3. 列出列名可以使您的代码更具自记录性,因此可能更具可读性。

  4. 如果您正在通过网络传输(或者即使不是),则不需要的列也很浪费。


7
“最重要的实际原因是它迫使用户神奇地知道将返回列的顺序。” 我看不出这是个问题。在任何现代DB客户端中,您都按名称而不是顺序读取列。
Josh Noe

我倾向于通过C接口运行SQL,所以我真的不知道“数据库客户端”中的最新技术是什么。但我认为您正在谈论的客户端类型可能正在执行一些非标准的非SQL魔术。(例如,在SQLite中,查询sqlite3_master以弄清楚如何将您更改*为一组名称。)
pkh

接下来,有多少人在使用列名索引的现代应用程序中编写代码?当然,大多数人肯定会使用某种映射器和一整堆缓存来存储允许过时的数据。就个人而言,请先编写代码,然后再担心是否会遇到性能问题。
科林·怀斯曼

10

指定列列表通常是最佳选择,因为如果有人在表中添加/插入列,您的应用程序将不会受到影响。


7

指定列名称绝对更快-对于服务器。但是如果

  1. 性能并不是一个大问题(例如,这是一个网站内容数据库,每个表中有数百行,也许是数千行,但没有数百万行);和
  2. 您的工作是使用通用框架创建许多小型的类似应用程序(例如,面向公众的内容管理网站),而不是创建复杂的一次性应用程序;和
  3. 灵活性很重要(为每个站点定制大量的数据库模式);

那么最好还是坚持使用SELECT *。在我们的框架中,大量使用SELECT *可以使我们向表中引入新的网站托管内容字段,从而为它提供CMS的所有优势(版本,工作流/批准等),而只需触摸代码即可。几分,而不是几分。

我知道数据库专家会为此而讨厌我-继续,请我投下赞成票-但是在我的世界中,开发人员时间很少,CPU周期也很充裕,所以我要相应地调整我所保存和浪费的东西。


1
它还使ORM更加易于使用。当通过传递查询构建对象来构建查询时,不必一定知道其他哪些代码部分(权限检查,您拥有什么)需要哪些列。因此,为了限制列数,每次查询需要编写时都需要进行调查。IMO,这毫无意义。当查询确实变慢(日志!)时,可以改善查询条件。
–bytepusher

6

即使查询不是通过网络发送的,SELECT *也是一种不好的做法。

  1. 选择比您需要的数据更多的数据会使查询效率降低-服务器必须读取和传输额外的数据,因此这会花费时间并给系统造成不必要的负载(不仅是网络(如其他人所提到的,而且还包括磁盘,CPU等)。 )。此外,服务器无法优化查询,并且可能无法对其进行优化(例如,对查询使用覆盖索引)。
  2. 一段时间后,您的表结构可能会更改,因此SELECT *将返回一组不同的列。因此,您的应用程序可能会获得意外结构的数据集并在下游某处中断。明确说明各列可确保您获得已知结构的数据集,或在数据库级别获得明确的错误(例如“未找到列”)。

当然,对于一个小型而简单的系统而言,所有这些都无关紧要。


4

在性能方面,具有特定列的SELECT可以更快(无需读取所有数据)。如果您的查询确实确实使用了所有列,则仍然建议使用带有显式参数的SELECT。任何速度差异基本上都是不明显的,并且接近恒定时间。有一天,您的架构将发生更改,这是防止出现此问题的良好保证。


您对这种不引人注目的错误是错误的,因为我对多个DB所做的检查很明显,即使选择所有列,选择每列都更快。在某些情况下,速度要快三倍。
shahar eldad

4

到目前为止,这里有很多充分的理由可以回答,这是另一个未提及的理由。

明确命名列将帮助您进行后续维护。在某个时候,您将进行更改或故障排除,并发现自己在问“该列在哪里使用”。

如果您有明确列出的名称,那么通过所有存储过程,视图等来查找对该列的每个引用都很简单。只需为您的数据库架构转储一个CREATE脚本,然后在其中搜索文本即可。


3

绝对定义列,因为SQL Server不必对列进行查询即可将其拉出。如果定义列,则SQL可以跳过该步骤。


这是:1)不相关,因为SQL Server必须以任何一种方式引用表架构(以验证列名或查找已知有效的列名)2)与所问的问题无关,因为在该问题中引用了所有列。提出的唯一问题是带有架构更改的脆弱性。
IDisposable

投票无效,因为无论如何都必须验证列。
约翰·吉布

3

总是最好指定所需的列,如果您三思而后行,则SQL不必在每次查询时都认为“ wtf is *”。最重要的是,稍后有人可能会向查询中实际上不需要的表中添加列,在这种情况下,通过指定所有列会更好。


1
这是不正确的:SQL Server仍必须解析每个列,并查看其是否存在于目录中,而它知道 “ *”可以(并且,*扩展为所有列)。无论哪种方式,这是很轻松的DBMS做任何一个(除非你有24000列),所以我敢打赌,这是相同的两种方式
马特Rogish

我认为更好的一点是,许多人不见了,不幸的是,这个答案仅次于地址,是如果发生架构/表更改(即添加了新列),则不会破坏事情。
肖恩·汉利

1
彻底检查一下,因为查找*扩展的列与验证提供的列名相同。
IDisposable

3

“选择*”的问题是可能带来不需要的数据。在实际的数据库查询期间,所选的列并没有真正添加到计算中。真正“繁重”的是将数据传输回客户端,而您真正不需要的任何列就是浪费网络带宽,并增加了等待查询返回的时间。

即使您确实使用了“ select * ...”中带来的所有列,也仅此而已。如果将来您更改表/视图的布局并添加更多列,即使您不需要它们,也将开始将它们纳入选择范围。

“ select *”语句不好的另一点是在视图创建上。如果使用“选择*”创建视图,然后在表中添加列,则视图定义和返回的数据将不匹配,并且需要重新编译视图以使它们再次起作用。

我知道写一个“ select *”很诱人,因为我真的不喜欢手动指定查询中的所有字段,但是当您的系统开始发展时,您会发现花额外的时间是值得的/努力指定字段,而不是花费更多的时间和精力来消除视图中的错误或优化应用程序。


VIEW的观点非常重要。如果向表中添加列,不仅不会得到所有列(尽管*会引起您的思考),而且它们甚至可能与表的实际布局都不匹配。
Euro Micelli

3

虽然显式列出列对性能有好处,但不要发疯。

因此,如果您使用所有数据,为简单起见,请尝试使用SELECT *(想象有许多列并执行JOIN ...查询可能会很糟糕)。然后-测量。与具有明确列出的列名的查询进行比较。

不要spec 性能,不要去衡量它!

当您有一些包含大数据的列(例如帖子或文章的正文)并且在给定查询中不需要它时,显式列表最有用。然后,通过不将其返回到答案数据库服务器中,可以节省时间,带宽和磁盘吞吐量。您的查询结果也将更小,这对任何查询缓存都非常有用。


3

您实际上应该只选择所需的字段,并且仅选择所需的数字,即

SELECT Field1, Field2 FROM SomeTable WHERE --(constraints)

在数据库外部,动态查询冒着注入攻击和数据格式错误的风险。通常,您可以使用存储过程或参数化查询来解决此问题。同样(尽管问题不大),每次执行动态查询时,服务器都必须生成一个执行计划。


我认为“每次执行动态查询时服务器都必须生成一个执行计划”,我认为这会使查询速度变慢。谢谢。
Ankur 2010年

使用动态sql的性能问题可能只会在非常高的负载情况下才能实现,Sql Server非常擅长高效地管理查询计划。
马修·阿伯特

2

如果使用*或列,则Select同样有效(就速度而言)。

区别在于内存,而不是速度。当您选择几列时,SQL Server必须分配内存空间来为您提供查询,包括您所请求的所有列的所有数据,即使您仅使用其中之一。

就性能而言重要的是执行计划,而执行计划又很大程度上取决于您的WHERE子句以及JOIN,OUTER JOIN等的数量...

对于您的问题,只需使用SELECT *。如果您需要所有列,则没有性能差异。


2

当且仅当您需要获取所有字段的数据时,使用显式字段名称相对于*并不是更快。

您的客户端软件不应该依赖于返回字段的顺序,所以这也是胡说八道。

而且有可能(尽管不太可能)需要使用*来获取所有字段,因为您尚不知道存在哪些字段(请考虑动态数据库结构)。

使用显式字段名的另一个缺点是,如果它们很多且很长,那么会使读取代码和/或查询日志变得更加困难。

因此,规则应为:如果需要所有字段,请使用*,如果仅需要一个子集,请明确命名它们。


2

结果太大。生成结果并将结果从SQL引擎发送到客户端的速度很慢。

作为通用编程环境的客户端,由于行数可能非常大(例如,数千万行),因此也不应(也不应)设计为过滤和处理结果(例如WHERE子句,ORDER子句)。


因此,如果您需要实际使用所有不同的列,那将是很好的……并且如果您的数据库和应用程序又位于同一台服务器上,那不会有太大的不同吗?
Ankur 2010年

@Ankur:即使在同一台服务器上,也需要通过数据库接口传输数据。
kennytm 2010年

2

只要您的列仍然存在(以任何顺序),为您希望进入应用程序的每个列命名也可以确保如果有人更改表,您的应用程序不会中断。


1

它取决于您的数据库服务器的版本,但是现代版本的SQL可以以任何一种方式缓存计划。我想说的是您的数据访问代码中最可维护的内容。


1

最好明确指出所需的列的一种最佳做法是,由于表结构将来可能会发生变化。

如果您使用基于索引的方法手动读取数据,以查询结果填充数据结构,那么将来在添加/删除列时,您将很难找出问题所在。

至于什么更快,我会请其他人提供专业知识。


1

与大多数问题一样,这取决于要实现的目标。如果要创建允许任何表中所有列的数据库网格,则“选择*”是答案。但是,如果您只需要某些列,并且很少在查询中添加或删除列,则分别指定它们。

它还取决于您要从服务器传输的数据量。如果其中一列被定义为备忘录,图形,blob等,而您不需要该列,则最好不要使用“选择*”,否则您将获得一大堆不需要的数据想要,您的表现可能会受到影响。


1

要补充其他人所说的内容,如果您选择的所有列都包含在索引中,那么您的结果集将从索引中提取,而不是从SQL查找其他数据。



1

上面每个人所说的,再加上:

如果您正在努力寻找可读的可维护代码,请执行以下操作:

SELECT foo,bar FROM小部件;

立即可读并显示意图。如果打了那个电话,您就会知道自己正在得到什么。如果小部件仅具有foo和bar列,则选择*意味着您仍然必须考虑要返回的内容,确认顺序已正确映射,依此类推。但是,如果小部件具有更多的列,但您仅对foo感兴趣和bar,那么当您查询通配符而仅使用返回的某些内容时,您的代码会变得混乱。


1

记住,如果定义上有内部联接,则不需要所有列,因为联接列中的数据是重复的。

这并不像在SQl服务器中列出列那样困难甚至费时。您只需将它们从对象浏览器中拖过即可(通过从字列中拖拽可以一劳永逸)。要给您的系统带来永久性的性能下降(因为这会减少索引的使用,并且因为通过网络发送不需要的数据的成本很高),并且更有可能在数据库更改时遇到意想不到的问题(有时会添加列您不希望用户看到例如)只是为了节省不到一分钟的开发时间,这是短视且不专业的。


1

在性能方面,我已经看到了两者相同的评论。但可用性方面有一些+和-

当您在查询中使用(选择*)时,如果有人更改了表并添加了上一个查询不需要的新字段,则这是不必要的开销。以及如果新添加的字段是Blob或image字段怎么办???那么您的查询响应时间将会非常慢。

另一方面,如果您使用(select col1,col2,..),并且表被更改并添加了新字段,并且如果结果集中需要这些字段,则在更改表后始终需要编辑选择查询。

但我建议始终在查询中使用select col1,col2,...,如果表稍后更改,则更改查询。


0

绝对定义每次要选择的列。没有理由不这样做,而且性能改进是值得的。

他们绝对不应该选择“ SELECT *”


0

如果需要每一列,则只需使用SELECT *,但请记住顺序可能会发生变化,因此当您使用结果时,请按名称而不是按索引访问它们。

我将忽略有关*需要如何获取列表的注释-机会是解析和验证命名列等于处理时间(如果不是更多的话)。不要过早优化;-)


0

在执行效率方面,我不知道有什么显着差异。但是为了提高程序员的效率,我会写字段名称,因为

  • 您知道顺序,如果您需要按数字索引,或者您的驱动程序对blob值表现得很滑稽,并且需要确定的顺序
  • 如果您应该添加更多字段,则只需阅读所需的字段
  • 如果您拼写错误或重命名字段,而不是记录集/行中的空值,则会出现sql错误
  • 您可以更好地了解正在发生的事情。

0

嘿,要切合实际。在原型制作时使用select *,在实现和部署时使用特定的列。从执行计划的角度来看,两者在现代系统上是相对相同的。但是,选择特定的列会限制必须从磁盘检索,存储在内存中并通过网络发送的数据量。

最终最好的计划是选择特定的列。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.