摘要:由于性能和可维护性问题,我在MS SQL中使用SELECT *看到了很多反对的建议。然而,这些帖子很多都很老 - 5到10年!很多帖子中的似乎,即使在他们的时间,性能问题实际上也可能非常小,而且可维护性问题(“哦不,如果有人更改列,并且您通过索引数组来获取数据!您的SELECT *会让您遇到麻烦!“),现代编码实践和ORM(如Dapper)似乎 - 至少在我的经验中 - 消除此类顾虑。
所以:SELECT *是否存在今天仍然存在的问题?
更大的背景:我已经开始在一个拥有大量旧代码(ASP脚本等)的地方工作,而且我一直在帮助实现大量的现代化,但是:我的大多数SQL经验实际上是来自MySQL和PHP框架和ORM - 这是我第一次使用MS SQL - 我知道两者之间存在细微差别。另外:我的同事比我年长一点,并且有一些担忧 - 对我来说 - 似乎“老了”。 (“可空的字段很慢!避免它们!”)但是又一次:在这个特定的领域,他们肯定比我更有经验。
出于这个原因,我还想问一下:现代ORM中的SELECT *是否安全,今天是不安全的,是否有最新的在线资源表明这样?
谢谢! :)
答案 0 :(得分:14)
我不会在这个答案中触及可维护性,只有性能部分。
此上下文中的性能与ORM几乎没有关系。
服务器如何生成正在运行的查询,无论是手动编写还是由ORM生成,都无关紧要。
选择您不需要的列仍然是一个坏主意。
从性能的角度来看,查询是否如下所示并不重要:
SELECT * FROM Table
或明确列出所有列,例如:
SELECT Col1, Col2, Col3 FROM Table
如果您只需要Col1
,请确保仅选择Col1
。无论是手工编写查询还是微调ORM,都无关紧要。
为什么选择不必要的列是一个坏主意:
从磁盘读取的额外字节
通过网络传输的额外字节
要在客户端上解析的额外字节
但是,最重要的原因是优化工具可能无法生成好的计划。例如,如果存在包含所有请求列的覆盖索引,则服务器通常只读取此索引,但如果您请求更多列,则会执行额外查找或使用其他索引,或者只扫描整个表。最终影响可以从可忽略不计到秒与运行时间之间变化。数据库越大越复杂,您就越有可能看到明显的差异。
Myth: Select * is bad网站上有关于此主题Use the index, Luke的详细文章。
现在我们已经就选择的原因建立了共识 一切都不利于性能,你可能会问为什么它被列为一个 神话?这是因为很多人认为明星是坏事。 此外,他们认为他们没有犯下这种罪行,因为他们的罪行 无论如何,ORM按名称列出所有列。事实上,犯罪是选择 所有列都没有考虑它 - 大多数ORM都很容易提交 这个罪行代表他们的用户。
我会在这里添加你的评论的答案。
我不知道如何处理一个没有选择哪个字段的ORM。我个人会尽量不使用它。通常,ORM添加了leaks严重的抽象层。 https://en.wikipedia.org/wiki/Leaky_abstraction
这意味着您仍然需要知道如何编写SQL代码以及DBMS如何运行此代码,还需要知道ORM如何工作并生成此代码。如果你选择不知道ORM背后发生了什么,那么当你的系统变得越来越琐碎时,你将会遇到无法解释的性能问题。
你说在你以前的工作中你使用ORM来处理一个没有问题的大型系统。它对你有用。好。不过,我有一种感觉,你的数据库并不是很大(你有数十亿行吗?)并且系统的性质允许隐藏缓存后面的性能问题(这并不总是可行的)。系统可能永远不会超出硬件容量。如果您的数据适合缓存,通常在任何情况下都会相当快。只有当你越过某个阈值时,它才开始变得重要。之后,一切都变得缓慢,很难解决。
业务/项目经理通常会忽略可能永远不会发生的未来可能出现的问题。企业总是有更紧迫的紧迫问题需要处理。如果业务/系统在性能成为问题时变得足够长,它将要么已经积累了足够的资源来重构整个系统,要么它将继续使用效率降低,或者如果系统恰好对业务非常关键,那么就失败了给另一家公司一个超越它的机会。
回答您的问题"是否在性能受到严重关注的应用中使用ORM"。当然你可以使用ORM。但是,您可能会发现它比不使用它更困难。考虑到ORM和性能,您必须手动检查ORM生成的SQL代码,并从性能的角度确保它是一个很好的代码。因此,您仍然需要了解您使用得非常好的SQL和特定DBMS,并且您需要非常了解ORM以确保它生成您想要的代码。为什么不直接编写你想要的代码?
您可能认为ORM与原始SQL的这种情况有点类似于高度优化的C ++编译器,而不是手动编写汇编程序中的代码。嗯,事实并非如此。在大多数情况下,现代C ++编译器确实会生成比汇编器中手动编写的代码更好的代码。但是,编译器非常了解处理器,优化任务的性质比数据库中的要简单得多。 ORM不知道您的数据量,它对您的数据分发一无所知。
top-n-per-group
的简单经典示例可以通过两种方式完成,最佳方法取决于只有开发人员知道的数据分布。如果性能很重要,即使您手动编写SQL代码,也必须知道DBMS如何工作并解释此SQL代码并以DBMS以最佳方式访问数据的方式布置代码。 SQL本身是一种高级抽象,可能需要微调以获得最佳性能(例如,SQL Server中有许多查询提示)。 DBMS有一些统计数据,它的优化器试图使用它,但它通常是不够的。
现在,在此之上添加另一层ORM抽象。
说完了这一切,"表现"是一个模糊的术语。所有这些问题在一定的阈值后变得重要。由于现代硬件非常好,这个门槛已被推到相当远的地方,以允许很多项目忽略所有这些问题。
实施例。对具有百万行的表的最佳查询在10毫秒内返回。非最佳查询在1秒内返回。慢100倍。最终用户会注意到吗?也许,但可能并不重要。将表增长到十亿行,或者一个用户拥有1000个并发用户。 1秒vs 100秒。最终用户肯定会注意到,即使比率(慢100倍)是相同的。实际上,随着数据的增长,这个比例会增加,因为各种缓存的用处越来越少。
答案 1 :(得分:6)
从SQL-Server-Performance-Point-of-view,你永远不应该使用select *
,因为这意味着sqlserver从磁盘或ram读取完整的行。即使您需要所有字段,我建议不要select *
,因为您不知道,谁将任何数据附加到您的应用程序不需要的表中。有关详细信息,请参阅@ sandip-patel的答案
从DBA角度来看:如果您准确提供所需的列名,dbadmin可以更好地分析和优化他的数据库。
从ORM-Point-Of-View更改列名,我建议不要使用select *
。你想知道,如果表格改变了。如果基础表发生变化,如果没有出现错误,您希望如何保证应用程序运行并给出正确的结果?
个人意见:我真的不能在需要表现良好的应用程序中使用ORM ......
答案 2 :(得分:5)
这个问题已经过了一段时间了,似乎没有人能够找到Ben正在寻找的......
我认为这是,因为答案是"它取决于"。
只有不是一个的答案。
实施例
我可以继续。没有一个答案。这取决于很多因素。
答案 3 :(得分:3)
通常最好明确选择列名。如果一个表收到一个额外的列,它将加载一个select *调用,其中不需要额外的列。
这可能有几个含义:
更多网络流量
更多I / O(必须从磁盘读取更多数据)
可能更多的I / O(不能使用覆盖索引 - 执行表扫描以获取数据)
可能更多的CPU(不能使用覆盖索引,因此数据需要排序)
<强>异常即可。 选择*正常的唯一位置是在Exists或Not Exists谓词子句之后的子查询中,如:
Select colA, colB
From table1 t1
Where Exists (Select * From Table2 Where column = t1.colA)
答案 4 :(得分:-1)
可维护性点。
如果您执行“从表中选择*”
然后我改变了表并添加了一列。
您的旧代码可能会崩溃,因为它现在有一个额外的列。
这会为将来的修订创建一个夜间母马,因为您必须识别select *的所有位置。
速度差异非常小,我不会担心。使用Varchar与Char之间存在速度差异,Char更快。但速度差异非常小,几乎不值得谈论。
选择*的最大问题是对表结构进行更改(添加)。
可维护性的噩梦。初级程序员的标志,以及糟糕的项目代码。话虽如此,我仍然使用select *但打算在我使用我的代码进行制作之前删除它。