performance/code-maintainability 围绕 SELECT * 在 MS SQL 上的担忧在今天仍然与现代 ORM 相关吗?

are performance/code-maintainability concerns surrounding SELECT * on MS SQL still relevant today, with modern ORMs?

总结:出于性能和可维护性方面的考虑,我看到很多建议反对 在 MS SQL 中使用 SELECT *。然而,其中许多帖子都非常古老——5 到 10 年了! 似乎,从这些帖子中的许多帖子来看,性能问题实际上可能非常小,即使在他们的时代也是如此,至于可维护性问题 ("oh no, what if someone changes the columns, and you were getting data by indexing an array! your SELECT * would get you in trouble!"),现代编码实践和 ORM(例如 Dapper)似乎 - 至少根据我的经验 - 可以消除这种担忧。

等等:SELECT * 是否存在今天仍然相关的问题?


更大的背景:我开始在一个有很多旧 MS 代码(ASP 脚本等)的地方工作,并且我一直在帮助实现很多现代化,但是: 我的大部分 SQL 经验实际上来自 MySQL 和 PHP 框架和 ORM - 这是我第一次使用 MS SQL - 我知道两者之间存在细微差别他们俩。另外:我的同事比我大一点,并且有一些担忧 - 对我来说 - 似乎 "older"。 ("nullable fields are slow! avoid them!") 但是再说一次:在这个特定领域,他们肯定比我更有经验。

出于这个原因,我还想问:SELECT * 在今天使用现代 ORM 是否安全和理智,是否有最近的在线资源表明这样?

谢谢! :)

我不会在这个答案中涉及可维护性,只涉及性能部分。

这种情况下的性能与 ORM 关系不大。

对于服务器来说,运行ning 的查询是如何生成的,是手写的还是由 ORM 生成的,这并不重要。

select 您不需要的列仍然是个坏主意。

从性能的角度来看,查询是否像这样并不重要:

SELECT * FROM Table

或所有列都明确列出,例如:

SELECT Col1, Col2, Col3 FROM Table

如果您只需要 Col1,那么请确保您只 select Col1。无论是通过手动编写查询还是通过微调您的 ORM 来实现,都没有关系。


为什么 select 添加不必要的列是个坏主意:

  • 要从磁盘读取的额外字节

  • 要通过网络传输的额外字节

  • 要在客户端上解析的额外字节

  • 但是,最重要的原因是优化器可能无法生成好的计划。例如,如果有一个包含所有请求列的覆盖索引,服务器通常只会读取这个索引,但是如果你请求更多的列,它会做额外的查找或使用其他索引,或者只扫描整个 table.最终影响可能从微不足道到几秒钟而不是几小时 运行 时间。数据库越大越复杂,您就越有可能看到明显的差异。

Myth: Select * is bad on the Use the index, Luke 网站上有一篇关于此主题的详细文章。

Now that we have established a common understanding of why selecting everything is bad for performance, you may ask why it is listed as a myth? It's because many people think the star is the bad thing. Further they believe they are not committing this crime because their ORM lists all columns by name anyway. In fact, the crime is to select all columns without thinking about it—and most ORMs readily commit this crime on behalf of their users.


我会在此处添加对您评论的回答。

我不知道如何处理一个 ORM,这个 ORM 没有给我一个选择要 select 的字段。我个人会尽量不使用它。一般来说,ORM增加了一层抽象,即leaks badly. https://en.wikipedia.org/wiki/Leaky_abstraction

意味着你还需要知道如何编写SQL代码以及DBMS如何运行编写这段代码,还需要知道ORM是如何工作并生成这段代码的。如果您选择不了解 ORM 背后发生的事情,那么当您的系统增长超出微不足道时,您将遇到无法解释的性能问题。

您说在您之前的工作中,您将 ORM 用于大型系统而没有出现问题。它对你有用。好的。不过,我有一种感觉,你的数据库并不是很大(你有数十亿行吗?)并且系统的性质允许隐藏缓存背后的性能问题(这并不总是可能的)。系统可能永远不会超出硬件容量。如果您的数据适合缓存,通常在任何情况下它都会相当快。只有当你跨过某个门槛时,它才开始变得重要。之后突然一切都变慢了,很难修复。

business/project 经理通常会忽略未来可能永远不会发生的问题。企业总是有更紧迫的紧迫问题需要处理。如果 business/system 在性能成为问题时增长足够大,它要么已经积累了足够的资源来重构整个系统,要么它会继续工作,但效率越来越低,或者如果系统恰好对业务非常关键,就失败了给其他公司赶超的机会。

正在回答您的问题 "whether to use ORMs in applications where performance is a large concern"。当然你可以使用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 抽象。

说了这么多,"performance" 是一个模糊的术语。所有这些担忧在一定的门槛之后变得很重要。由于现代硬件非常好,这个门槛已经被推得相当远,以允许许多项目忽略所有这些问题。

例子。在 10 毫秒内对具有百万行 returns 的 table 进行最佳查询。 1 秒内的非最佳查询 returns。慢100倍。最终用户会注意到吗?也许,但可能并不重要。将 table 增加到十亿行,或者有 1000 个并发用户而不是一个用户。 1 秒对 100 秒。最终用户肯定会注意到,即使比率(慢 100 倍)相同。实际上这个比例会随着数据的增长而增加,因为各种缓存会变得越来越没用。

明确 select 列名通常是更好的主意。如果 table 收到一个额外的列,它将通过 select * 调用加载,其中不需要额外的列。

这可能有几个含义:

  • 更多网络流量

  • 更多 I/O(必须从磁盘读取更多数据)

  • 可能更多 I/O(不能使用覆盖索引 - 执行 table 扫描以获取数据)

  • 可能更多CPU(不能使用覆盖索引,所以数据需要排序)

异常Select * OK 的唯一地方是在 Exists 或 Not Exists 谓词子句之后的子查询中,如:

Select colA, colB
From table1 t1
Where Exists (Select * From Table2  Where column = t1.colA)

More Details -1

More Details -2

More Details -3

从 SQL-Server-Performance-Point-of-view 来看,您永远不应该使用 select *,因为这意味着 sqlserver 从磁盘或 ram 读取完整的行。即使您需要所有字段,我也建议您不要执行 select *,因为您不知道,是谁将您的应用程序不需要的任何数据附加到 table。有关详细信息,请参阅@sandip-patel

的回答

从 DBA 的角度来看:如果您准确地给出了您需要的那些列名,dbadmin 可以更好地分析和优化他的数据库。

从更改列名的 ORM 观点来看,我建议不要使用 select *。您想知道 table 是否更改。如果基础 table 发生变化而您没有得到错误,您想如何保证您的应用程序 运行 并给出正确的结果??

个人意见:我真的不在需要良好性能的应用程序中使用 ORM...

可维护性点。

如果你做 "Select * from Table"

然后我更改 Table 并添加一列。

您的旧代码可能会崩溃,因为它现在多了一个列。

这会给以后的修订造成噩梦,因为您必须确定 select *.

的所有位置

速度差异很小,我不会担心。使用 Varchar 与 Char 存在速度差异,Char 更快。但速度差异是如此之小,以至于不值得谈论。

Select * 最大的问题是对 table 结构的更改(添加)。

可维护性噩梦。初级程序员的标志,以及糟糕的项目代码。话虽如此,我仍然使用 select * 但打算在使用我的代码投入生产之前将其删除。

这个问题已经有一段时间了,似乎没有人能够找到 Ben 在找什么...

我认为是,因为答案是"it depends"。

只有没有一个回答这个问题。

例子

  • 正如我之前指出的,如果一个数据库不是你的,并且它可能经常被改变,你不能保证性能,因为 select * 每行的数据量可能会爆炸
  • 如果您使用自己的数据库编写应用程序,没有人会更改您的数据库(希望如此)并且您需要您的列,所以 select *
  • 有什么问题
  • 如果你构建某种延迟加载 "main properties" bee 立即加载而其他 bee 稍后加载(同一实体),你不能使用 select * 因为你得到所有
  • 如果您使用 select * 其他开发人员每次都会考虑 "did he think about select *",因为他们会尝试优化。所以你应该添加足够的评论...
  • 如果你构建 3 层应用程序在中间层构建大型缓存并且性能是缓存完成的主题,你可以使用 select *
  • 扩展 3 层:如果您有很多并发用户 and/or 非常大的数据,您应该考虑每个字节,因为您必须扩展中间层,因为每个字节都被浪费了(正如有人指出的那样在评论之前出来)
  • 如果您为 3 个用户和数千条记录构建一个小型应用程序,预算可能没有时间进行优化 speed/db-layout/something
  • 与您的 dba 交谈...他会建议您必须 changed/optimized/stripped 向下/...

我可以继续。只是没有一个答案。这取决于很多因素。