为什么SELECT *是不好的做法?如果您添加了想要的新列,这难道不意味着需要更改的代码更少吗?
我知道SELECT COUNT(*)在某些db上是一个性能问题,但是如果您真的想要每个列呢?
为什么SELECT *是不好的做法?如果您添加了想要的新列,这难道不意味着需要更改的代码更少吗?
我知道SELECT COUNT(*)在某些db上是一个性能问题,但是如果您真的想要每个列呢?
当前回答
如果您真的想要每个列,我没有看到select(*)和命名列之间的性能差异。命名列的驱动程序可能只是为了明确您希望在代码中看到哪些列。
但是,通常情况下,您不希望每个列和select(*)会导致数据库服务器做不必要的工作,并且必须通过网络传递不必要的信息。它不太可能造成明显的问题,除非系统被大量使用或网络连接很慢。
其他回答
SELECT语句中的星号“*”是查询中涉及的表中所有列的简写。
性能
简写可能会慢一些,因为:
并不是所有的字段都被索引,这迫使对整个表进行扫描——效率较低 保存到通过线路发送SELECT *的内容可能会导致全表扫描 返回比需要的更多的数据 使用可变长度数据类型返回尾随列会导致搜索开销
维护
当使用SELECT *时:
Someone unfamiliar with the codebase would be forced to consult documentation to know what columns are being returned before being able to make competent changes. Making code more readable, minimizing the ambiguity and work necessary for people unfamiliar with the code saves more time and effort in the long run. If code depends on column order, SELECT * will hide an error waiting to happen if a table had its column order changed. Even if you need every column at the time the query is written, that might not be the case in the future the usage complicates profiling
设计
SELECT *是一个反模式:
查询的目的不那么明显;应用程序使用的列是不透明的 它打破了尽可能使用严格类型的模块化规则。Explicit几乎在所有情况下都更好。
什么时候应该使用“SELECT *”?
如果显式地需要涉及表中的每一列,而不是编写查询时存在的每一列,则使用SELECT *是可以接受的。数据库将在内部将*展开为完整的列列表—没有性能差异。
否则,显式地列出要在查询中使用的每一列—最好是在使用表别名时。
可以把它看作是减少应用程序和数据库之间的耦合。
总结一下“代码气味”方面: SELECT *在应用程序和模式之间创建一个动态依赖关系。限制它的使用是使依赖更加明确的一种方法,否则对数据库的更改更有可能使应用程序崩溃。
这里有一个重要的区别,我认为大多数答案都忽略了。
SELECT *不是问题。返回SELECT *的结果是问题所在。
举个例子,在我看来:
WITH data_from_several_tables AS (
SELECT * FROM table1_2020
UNION ALL
SELECT * FROM table1_2021
...
)
SELECT id, name, ...
FROM data_from_several_tables
WHERE ...
GROUP BY ...
...
这避免了大多数答案中提到的使用SELECT *的所有“问题”:
读取的数据比预期的多?现代数据库中的优化器会意识到实际上并不需要所有列 源表的列顺序会影响输出吗?我们仍然选择和 显式返回数据。 消费者不能看到他们从SQL?您所操作的列在代码中是显式的。 索引可能不被使用?同样,现代优化器应该处理这个问题,就像我们没有选择*一样
这里有一个可读性/可重构性的优势——不需要重复很长的列列表或其他常见的查询子句(如过滤器)。如果在使用SELECT *和SELECT <columns>(在绝大多数情况下-显然总是在关键情况下配置运行代码)时,查询计划有任何不同,我会感到惊讶。
在设计方案之前理解您的需求(如果可能的话)。
了解数据, 1)索引 2)所使用的存储类型; 3)供应商引擎或功能;即……缓存,内存功能 4)数据类型 5)桌子大小 6)查询频率 7)如果资源是共享的,相关的工作量 8)测试
A)要求会有所不同。如果硬件不能支持预期的工作负载,则应该重新评估如何在工作负载中提供需求。关于向表中添加的列。如果数据库支持视图,您可以使用特定的命名列创建特定数据的索引(?)视图(vs.选择'*')。定期检查您的数据和模式,以确保您永远不会遇到“输入垃圾”->“输出垃圾”综合征。
假设没有其他解;你可以考虑以下几点。一个问题总是有多种解决方案。
1)索引:select *将执行一个表罐。根据各种因素,这可能涉及到磁盘寻道和/或与其他查询的争用。如果表是多用途的,请确保所有查询都是高性能的,并在您的目标时间以下执行。如果有大量数据,而您的网络或其他资源没有调优;你需要考虑到这一点。数据库是一个共享环境。
2)存储类型。Ie:如果你使用SSD,磁盘或内存。I/O时间和系统/cpu上的负载会有所不同。
3) DBA是否可以调优数据库/表以获得更高的性能?假设出于某种原因,团队已经决定选择“*”是问题的最佳解决方案;可以将DB或表加载到内存中。(或者其他方法…也许反应被设计成有2-3秒的延迟?——而广告的作用是为公司赚取收入……)
4)从基线开始。了解您的数据类型,以及如何显示结果。更小的数据类型、字段数量会减少结果集中返回的数据量。这将为其他系统需求留下可用资源。系统资源通常是有限制的;“总是”工作低于这些限制,以确保稳定性和可预测的行为。
5)表/数据的大小。选择“*”在小表中很常见。它们通常适合内存,并且响应时间很快。再次……回顾您的需求。特征蠕变计划;总是为当前和未来可能的需求做计划。
6)查询/查询频率。了解系统上的其他工作负载。如果这个查询每秒发出一次,并且表很小。结果集可以设计为保留在缓存/内存中。然而,如果查询是一个频繁的批处理过程,有千兆字节/兆兆字节的数据……最好分配额外的资源以确保其他工作负载不受影响。
7) Related workloads. Understand how the resources are used. Is the network/system/database/table/application dedicated, or shared? Who are the stakeholders? Is this for production, development, or QA? Is this a temporary "quick fix". Have you tested the scenario? You'll be surprised how many problems can exist on current hardware today. (Yes, performance is fast...but the design/performance is still degraded.) Does the system need to performance 10K queries per second vs. 5-10 queries per second. Is the database server dedicated, or do other applications, monitoring execute on the shared resource. Some applications/languages; O/S's will consume 100% of the memory causing various symptoms/problems.
8)测试:测试你的理论,尽可能多地理解。你选择的“*”问题可能是一个大问题,或者它可能是你甚至不需要担心的事情。
使用列名进行选择提高了数据库引擎从索引访问数据的可能性,而不是查询表数据。
当数据库模式发生变化时,SELECT *使您的系统暴露在意想不到的性能和功能变化中,因为您要将任何新列添加到表中,即使您的代码还没有准备好使用或显示这些新数据。