哪个更快/最好? SELECT * 或 SELECT column1、colum2、column3 等
Posted
技术标签:
【中文标题】哪个更快/最好? SELECT * 或 SELECT column1、colum2、column3 等【英文标题】:Which is faster/best? SELECT * or SELECT column1, colum2, column3, etc 【发布时间】:2010-09-09 02:15:19 【问题描述】:我听说在编写 SQL 命令时使用SELECT *
通常是不好的做法,因为它对您特别需要的SELECT
列更有效。
如果我需要SELECT
表中的每一列,我应该使用
SELECT * FROM TABLE
或
SELECT column1, colum2, column3, etc. FROM TABLE
在这种情况下效率真的很重要吗?如果您真的需要所有数据,我认为SELECT *
在内部会更优化,但我是在对数据库没有真正了解的情况下这么说的。
我很想知道在这种情况下最佳做法是什么。
更新:我可能应该指定我真正想要执行SELECT *
的唯一情况是当我从一个表中选择数据时即使添加了新列,也始终需要检索所有列。
然而,鉴于我所看到的回复,这似乎仍然是一个坏主意,并且 SELECT *
永远不应该用于我曾经想过的更多技术原因。
【问题讨论】:
另见:***.com/questions/208925/… 见:***.com/questions/262450/…***.com/questions/65512/…***.com/questions/413819/select-except***.com/questions/208925/…***.com/questions/487578/…***.com/questions/1433971/… 是的,它是大部分的副本。 【参考方案1】:选择特定列更好的一个原因是它提高了 SQL Server 可以从索引访问数据而不是查询表数据的可能性。
这是我写的一篇文章:The real reason select queries are bad index coverage
更改也不那么脆弱,因为任何使用数据的代码都将获得相同的数据结构,无论您将来对表架构进行什么更改。
【讨论】:
为此 +1。如果引用的所有列都存在于单个索引(“覆盖索引”)中,那么您就成功了。 这不是他的问题的答案 - “如果我需要 SELECT 表中的每一列,...” - 在这种情况下,* vs col1, .., coln 无关紧要(但它确实适用于程序员的时间,因为 * 更短!)。 这仍然很重要,因为选择列表是一种契约形式,特别是如果 SQL 在存储过程中。 虽然 Jon 所说的完全正确,而且是非常有效的观点,但我必须同意 AS ASKED 的问题是关于他们是否已经要求所有列。由于这部分问题,真正的问题是面对架构更改时的脆弱性。 @MattRogish 先生,您没看错,这两种方法之间是否存在性能差异(*vs
all_column_names),而我们有数千行并且我们使用索引执行 SELECT(在 WHERE 子句中)? ?【参考方案2】:
鉴于您的说明您正在选择所有列,因此此时几乎没有区别。然而,要意识到数据库模式确实发生了变化。如果您使用 SELECT *
,您将获得添加到表中的任何新列,即使您的代码很可能还没有准备好使用或呈现这些新数据。这意味着您将系统暴露在意外的性能和功能变化中。
您可能愿意将此视为一笔不小的成本,但要意识到您不需要的列仍然必须是:
-
从数据库中读取
通过网络发送
编组到您的流程中
(对于 ADO 类型的技术)保存在内存中的数据表中
忽略和丢弃/垃圾收集
第 1 项有许多隐藏的成本,包括消除一些潜在的覆盖索引、导致数据页面加载(和服务器缓存抖动)、导致本可以避免的行/页面/表锁定。
将此与指定列与 *
的潜在节省进行平衡,唯一潜在的节省是:
-
程序员无需重新访问 SQL 即可添加列
SQL 的网络传输更小/更快
SQL Server 查询解析/验证时间
SQL Server 查询计划缓存
对于第 1 项,实际情况是您将添加/更改代码以使用您可能添加的任何新列,所以这是一个清洗。
对于第 2 项,差异很少足以将您推入不同的数据包大小或网络数据包数量。如果您到了 SQL 语句传输时间是主要问题的地步,您可能需要首先降低语句速率。
对于第 3 项,由于 *
的扩展无论如何都必须发生,这意味着无论如何都要咨询表架构,因此不会节省任何费用。实际上,列出列将产生相同的成本,因为它们必须针对架构进行验证。换句话说,这是一次彻底的清洗。
对于第 4 项,当您指定特定列时,您的查询计划缓存可能会变大,但仅如果您正在处理不同的列集(这不是您指定的)。在这种情况下,您确实需要不同的缓存条目,因为您需要根据需要使用不同的计划。
因此,由于您指定问题的方式,这一切都归结为面对最终架构修改时的问题弹性。如果您将此模式刻录到 ROM 中(发生这种情况),那么 *
是完全可以接受的。
但是,我的一般指导原则是您应该只选择您需要的列,这意味着有时看起来您正在要求所有这些列,但是 DBA 和架构演变意味着一些可能会出现新的列,这可能会极大地影响查询。
我的建议是您应该始终选择特定列。请记住,您会一遍又一遍地擅长自己的工作,因此请养成正确做事的习惯。
如果您想知道为什么架构可能会在不更改代码的情况下发生更改,请考虑审计日志、生效/到期日期以及 DBA 为系统性地解决合规性问题而添加的其他类似内容。另一个不正当更改的来源是系统其他地方或用户定义字段中性能的非规范化。
【讨论】:
“现实情况是,您将添加/更改代码以使用您可能添加的任何新列,因此这是一个洗牌。” - 仅当您在代码中按名称手动读取每一列时。如果您使用的是自动映射,则情况并非如此,并且此问题变得很重要。【参考方案3】:您应该只选择您需要的列。即使您需要所有列,最好列出列名,这样 sql server 就不必查询系统表的列。
此外,如果有人向表中添加列,您的应用程序可能会中断。你的程序也会得到它没有预料到的列,它可能不知道如何处理它们。
除此之外,如果表有一个二进制列,那么查询会更慢并且使用更多的网络资源。
【讨论】:
啊哈,所以通过使用 * 您正在为数据库添加额外的工作。好的,这是我没想到的原因之一。 +1 表示早期破坏/发现错误的风险。我认为关于效率的讨论是有效的,但 YAGNI。 SQL server 是否不需要验证或检查“col1”是否在指定的表中,即查询系统表? 最大的性能损失可能与索引有关。如果您要查找的列是用于查找数据的索引的一部分,则服务器将在那里获取数据,如果您执行 select * 它很可能必须执行所谓的书签查找,这需要额外的扫描以查找您甚至可能不需要的其余基础数据。 @Patrick - 现场。避免使用 * 的理由有很多,但这不是其中之一。【参考方案4】:select *
是坏事有四大原因:
最重要的实际原因是它迫使用户神奇地知道返回列的顺序。最好是明确的,这也可以保护您免受表格更改的影响,这很好地融入...
如果您正在使用的列名发生更改,最好尽早(在 SQL 调用时)捕获它,而不是在您尝试使用不再存在(或已经存在)的列时它的名字改变了,等等)
列出列名可以使您的代码更加自我记录,因此可能更具可读性。
如果您通过网络传输(或者即使您不是),您不需要的列只是浪费。
【讨论】:
“最重要的实际原因是它迫使用户神奇地知道返回列的顺序。”我不明白这是一个问题。在任何现代数据库客户端中,您都是按名称而不是顺序读取列。 我倾向于通过 C 接口运行我的 SQL,所以我真的不知道“数据库客户端”的最新技术是什么。但我认为您所说的客户端可能正在做一些非标准的非 SQL 魔术。 (例如,在 SQLite 中,查询 sqlite3_master 以找出如何将您的*
更改为一组名称。)
还有多少人在现代应用程序中编写使用列名索引的代码?大多数人肯定会使用某种映射器和一大堆缓存来存储允许过时的数据。个人而言,先写代码,以后有性能问题再担心。
@JoshNoe “我不明白这是怎么回事。在任何现代数据库客户端中,您都是按名称而不是顺序读取列。”这取决于性能对您是否重要。虽然您可以按名称而不是按顺序访问应用中的列,但如果按顺序访问列会更快。【参考方案5】:
指定列列表通常是最好的选择,因为如果有人向表中添加/插入列,您的应用程序不会受到影响。
【讨论】:
【参考方案6】:指定列名肯定更快 - 对于服务器。但是如果
-
性能不是大问题(例如,这是一个网站内容数据库,每个表中有数百行,可能是数千行,但不是数百万行);和
您的工作是使用通用框架创建许多类似的小型应用程序(例如面向公众的内容管理网站),而不是创建复杂的一次性应用程序;和
灵活性很重要(为每个站点定制大量的数据库架构);
那么你最好坚持使用 SELECT *。在我们的框架中,大量使用 SELECT * 允许我们将一个新的网站管理内容字段引入到一个表中,使其具有 CMS 的所有好处(版本控制、工作流/批准等),同时只触及代码几个点,而不是几十个点。
我知道 DB 专家会因此而恨我 - 继续吧,投我反对票 - 但在我的世界里,开发人员的时间很稀缺,CPU 周期很充裕,所以我会相应地调整我节省的和浪费的。
【讨论】:
它还使 ORM 更易于使用。当通过传递查询构建对象来构建查询时,不一定知道代码的其他部分需要哪些列(权限检查,你有什么)。因此,为了限制列,每次需要编写查询时都需要进行调查。这是没有意义的,IMO。当查询确实变得很慢(日志!)时,可以改进这些。【参考方案7】:即使查询不是通过网络发送的,SELECT * 也是一种不好的做法。
-
选择比您需要的更多的数据会使查询效率降低 - 服务器必须读取和传输额外的数据,因此需要时间并在系统上产生不必要的负载(不仅是网络,如其他人所提到的,还有磁盘, CPU 等)。此外,服务器无法尽可能优化查询(例如,为查询使用覆盖索引)。
一段时间后,您的表结构可能会发生变化,因此 SELECT * 将返回一组不同的列。因此,您的应用程序可能会获得一个具有意外结构的数据集并在下游某处中断。明确说明列可确保您获得已知结构的数据集,或在数据库级别获得明显错误(如“未找到列”)。
当然,对于一个小而简单的系统来说,这一切都无关紧要。
【讨论】:
【参考方案8】:在性能方面,带有特定列的 SELECT 可以更快(无需读取所有数据)。如果您的查询确实使用了所有列,则仍然首选带有显式参数的 SELECT。任何速度差异基本上是不明显的并且接近恒定时间。有一天,您的架构会发生变化,这是防止由此引起的问题的良好保险。
【讨论】:
您对不明显的情况是错误的,因为从我对几个数据库进行的检查中可以清楚地看出,选择每一列(即使是所有列)要快得多。在某些情况下,速度会***倍。【参考方案9】:到目前为止,这里已经回答了很多很好的理由,这里还有一个没有提到的理由。
明确命名列将帮助您进行后续维护。在某些时候,您将进行更改或故障排除,并发现自己在问“该列到底在哪里使用”。
如果您明确列出了名称,那么通过所有存储过程、视图等查找对该列的每个引用都很简单。只需为您的数据库模式转储一个 CREATE 脚本,然后通过它进行文本搜索。
【讨论】:
【参考方案10】:明确定义列,因为 SQL Server 不必对列进行查找来提取它们。如果您定义了列,那么 SQL 可以跳过该步骤。
【讨论】:
这是:1) 不相关,因为 SQL Server 必须以任何一种方式引用表架构(以验证列名或查找已知有效的列名)2) 与提出的问题无关,其中所有列都被引用。唯一的问题是架构更改时的脆弱性。 投反对票,因为无论如何它都必须验证列。【参考方案11】:指定你需要的列总是更好,如果你考虑一次,SQL不必每次查询时都认为“wtf is *”。最重要的是,稍后有人可能会向表中添加您在查询中实际上不需要的列,在这种情况下,您最好指定所有列。
【讨论】:
这不是真的:SQL 服务器仍然必须解析每一列并查看它是否存在于目录中,而它知道 "*" 确实(是的,* 是扩展到所有列)。无论哪种方式,DBMS 都可以轻松完成其中任何一项(除非您有 24,000 列),所以我敢打赌这两种方式都是一样的 我认为更好的一点是很多都丢失了,不幸的是,这个答案只是次要的,如果模式/表发生变化(即添加新列)它不会破坏事情。跨度> 这是一个彻底的清洗,因为查找 * 扩展的列与验证提供的列名相同。【参考方案12】:“选择 *”的问题在于可能会带来您并不真正需要的数据。在实际的数据库查询期间,选定的列并没有真正添加到计算中。真正“繁重”的是数据传输回您的客户端,而您并不真正需要的任何列都只会浪费网络带宽并增加您等待查询返回的时间。
即使您确实使用了从“选择 *...”中获取的所有列,也只是暂时的。如果将来您更改表格/视图布局并添加更多列,即使您不需要它们,您也会开始将它们带入您的选择中。
另一个“select *”语句不好的地方是视图创建。如果您使用“select *”创建视图,然后在表中添加列,则视图定义和返回的数据将不匹配,您需要重新编译视图才能再次工作。
我知道写一个“select *”很诱人,因为我真的不喜欢手动指定查询中的所有字段,但是当你的系统开始发展时,你会发现这是值得的这些额外的时间/精力用于指定字段,而不是花费更多的时间和精力来消除视图中的错误或优化您的应用程序。
【讨论】:
关于 VIEWs 的观点非常重要。如果将列添加到表中,您不仅不会获得所有列(尽管 * 会让您认为),而且它们甚至可能与表的实际布局不匹配。【参考方案13】:虽然明确列出列对性能有好处,但不要发疯。
因此,如果您使用所有数据,请尝试 SELECT * 为简单起见(想象有很多列并执行 JOIN... 查询可能会变得很糟糕)。然后 - 测量。与明确列出列名的查询进行比较。
不要推测性能,衡量它!
当您有一些包含大数据的列(如帖子或文章的正文)并且在给定的查询中不需要它时,显式列表最有帮助。然后通过不在您的答案数据库服务器中返回它可以节省时间、带宽和磁盘吞吐量。您的查询结果也会更小,这对任何查询缓存都有好处。
【讨论】:
【参考方案14】:您真的应该只选择您需要的字段,并且只选择所需的数字,即
SELECT Field1, Field2 FROM SomeTable WHERE --(constraints)
在数据库之外,动态查询存在注入攻击和畸形数据的风险。通常,您可以使用存储过程或参数化查询来解决这个问题。此外(虽然问题不大)服务器必须在每次执行动态查询时生成执行计划。
【讨论】:
“每次执行动态查询时,服务器都必须生成一个执行计划”,我认为这会减慢查询速度。谢谢。 使用动态sql的性能问题可能只有在负载非常高的场景下才会出现,Sql Server在高效管理查询计划方面做得很好。【参考方案15】:如果您使用 * 或列,则选择同样有效(就速度而言)。
区别在于内存,而不是速度。当您选择多个列时,SQL Server 必须分配内存空间来为您提供查询服务,包括您请求的所有列的所有数据,即使您只使用其中之一。
就性能而言,重要的是执行计划,而这又在很大程度上取决于您的 WHERE 子句以及 JOIN、OUTER JOIN 等的数量...
对于您的问题,只需使用 SELECT *。如果您需要所有列,则没有性能差异。
【讨论】:
【参考方案16】:当且仅当您需要获取所有字段的数据时,使用显式字段名称与 * 相比并不快。
您的客户端软件不应该依赖于返回字段的顺序,所以这也是无稽之谈。
您可能(尽管不太可能)需要使用 * 获取所有字段,因为您还不知道存在哪些字段(想想非常动态的数据库结构)。
使用显式字段名的另一个缺点是,如果字段名很多而且很长,那么阅读代码和/或查询日志就会变得更加困难。
所以规则应该是:如果您需要所有字段,请使用 *,如果您只需要一个子集,请明确命名。
【讨论】:
【参考方案17】:结果太大了。从 SQL 引擎生成结果并将结果发送到客户端的速度很慢。
作为通用编程环境的客户端不是也不应该被设计为过滤和处理结果(例如 WHERE 子句、ORDER 子句),因为行数可能很大(例如数千万行)。
【讨论】:
因此,如果您需要实际使用所有不同的列,那会很好......如果您的数据库和应用程序再次位于同一台服务器上,那并没有太大区别?跨度> @Ankur:即使在同一台服务器上,通过数据库接口传输数据也是有成本的。【参考方案18】:为您希望在应用程序中获得的每一列命名还可以确保如果有人更改表格,只要您的列仍然存在(以任何顺序),您的应用程序就不会中断。
【讨论】:
【参考方案19】:在性能方面,我见过两者相等的 cmets。但是可用性方面有一些+和-的
当您在查询中使用 (select *) 并且有人更改表并添加上一个查询不需要的新字段时,这是不必要的开销。如果新添加的字段是 blob 或图像字段怎么办???你的查询响应时间会很慢。
另一方面,如果您使用 (select col1,col2,..) 并且表被更改并添加了新字段,并且如果结果集中需要这些字段,则您始终需要在表后编辑您的选择查询改变。
但我建议始终在您的查询中使用 select col1,col2,... 并在以后更改表时更改查询...
【讨论】:
【参考方案20】:这取决于您的数据库服务器的版本,但现代版本的 SQL 可以以任何方式缓存计划。我会说使用您的数据访问代码最易于维护的任何内容。
【讨论】:
【参考方案21】:最好准确说明您想要的列的一个原因是表结构将来可能会发生变化。
如果您使用基于索引的方法手动读取数据,以使用查询结果填充数据结构,那么将来当您添加/删除列时,您会很头疼,试图找出问题所在。
至于什么更快,我会听从其他人的专业知识。
【讨论】:
【参考方案22】:与大多数问题一样,这取决于您想要达到的目标。如果您想创建一个允许任何表中的所有列的数据库网格,那么“选择 *”就是答案。但是,如果您只需要某些列并且很少从查询中添加或删除列,那么请单独指定它们。
这还取决于您要从服务器传输的数据量。如果其中一列被定义为备忘录、图形、blob 等,并且您不需要该列,则最好不要使用“Select *”,否则您将获得一大堆您不需要的数据想要,你的表现可能会受到影响。
【讨论】:
【参考方案23】:补充一下其他人所说的,如果您选择的所有列都包含在索引中,则将从索引中提取您的结果集,而不是从 SQL 中查找其他数据。
【讨论】:
【参考方案24】:如果想要获取列数等元数据,SELECT * 是必需的。
【讨论】:
【参考方案25】:我会为此受到抨击,但我会选择 *,因为几乎我所有的数据都是从 SQL Server 视图中检索出来的,这些视图将多个表中的所需值预先组合到一个易于访问的视图中。
然后,我确实希望视图中的所有列在将新字段添加到基础表时都不会更改。这还有一个额外的好处,就是允许我更改数据的来源。视图中的 FieldA 可能会被计算一次,然后我可以将其更改为静态的。无论哪种方式,View 都会向我提供 FieldA。
它的美妙之处在于它允许我的数据层获取数据集。然后它将它们传递给我的 BL,然后它可以从它们创建对象。我的主应用程序只知道对象并与之交互。我什至允许我的对象在传递数据行时自行创建。
当然,我是唯一的开发者,所以这也有帮助:)
【讨论】:
【参考方案26】:上面的人都说了,加上:
如果您正在努力获得可读可维护的代码,请执行以下操作:
SELECT foo, bar FROM 小部件;
立即可读并显示意图。如果你打那个电话,你就知道你会得到什么。如果小部件只有 foo 和 bar 列,则选择 * 意味着您仍然需要考虑返回的内容,确认订单映射正确等。但是,如果小部件有更多列但您只对 foo 感兴趣和 bar,那么当您查询通配符然后只使用返回的部分内容时,您的代码就会变得混乱。
【讨论】:
【参考方案27】:请记住,如果根据定义您有内部连接,则不需要所有列,因为连接列中的数据是重复的。
在 SQl 服务器中列出列并不困难甚至耗时。您只需从对象浏览器中拖动它们(您可以通过从单词列中拖动来一次性完成)。对您的系统造成永久性的性能影响(因为这可以减少索引的使用,并且因为通过网络发送不需要的数据代价高昂),并且在数据库更改时更有可能出现意外问题(有时会添加列例如,您不希望用户看到)只是为了节省不到一分钟的开发时间是短视和不专业的。
【讨论】:
【参考方案28】:这是一个旧帖子,但仍然有效。作为参考,我有一个非常复杂的查询,包括:
12张桌子 6 左连接 9 个内连接 所有 12 个表共有 108 列 我只需要54列 一个 4 列的 Order By 子句当我使用 Select * 执行查询时,平均需要 2869 毫秒。 当我使用 Select 执行查询时,平均需要 1513 毫秒。
返回的总行数为 13,949。
毫无疑问,选择列名意味着比 Select * 更快的性能
【讨论】:
【参考方案29】:每次都绝对定义要选择的列。没有理由不这样做,性能提升非常值得。
他们不应该选择“SELECT *”
【讨论】:
【参考方案30】:如果您需要每一列,则只需使用 SELECT *,但请记住,顺序可能会发生变化,因此当您使用结果时,按名称而不是按索引访问它们。
我会忽略 cmets 关于 * 需要如何获取列表 - 解析和验证命名列的机会等于处理时间,如果不是更多的话。不要过早地优化 ;-)
【讨论】:
以上是关于哪个更快/最好? SELECT * 或 SELECT column1、colum2、column3 等的主要内容,如果未能解决你的问题,请参考以下文章
Android SQLite:哪个查询(“query”或“rawQuery”)更快?
哪个更快,select * from table where id=a