为什么SELECT *是不好的做法?如果您添加了想要的新列,这难道不意味着需要更改的代码更少吗?

我知道SELECT COUNT(*)在某些db上是一个性能问题,但是如果您真的想要每个列呢?


当前回答

这里有一个重要的区别,我认为大多数答案都忽略了。

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>(在绝大多数情况下-显然总是在关键情况下配置运行代码)时,查询计划有任何不同,我会感到惊讶。

其他回答

当您只需要几列时使用SELECT *意味着传输的数据比您需要的多得多。这增加了数据库上的处理,并增加了将数据获取到客户端的延迟。此外,它在加载时将使用更多内存,在某些情况下会使用更多内存,例如大型BLOB文件,这主要是关于效率。

然而,除此之外,在查看查询时更容易看到正在加载哪些列,而不必查找表中的内容。

是的,如果您确实添加了一个额外的列,它会更快,但在大多数情况下,您希望/需要使用查询更改代码以接受新列,并且有可能获得您不想要/期望的列会导致问题。例如,如果获取所有列,然后依赖循环中的顺序来分配变量,然后再添加一个,或者如果列的顺序发生了变化(从备份恢复时就发生过这种情况),它可能会丢弃所有内容。

这也是为什么在执行INSERT操作时总是要指定列的原因。

如果您真的想要每个列,我没有看到select(*)和命名列之间的性能差异。命名列的驱动程序可能只是为了明确您希望在代码中看到哪些列。

但是,通常情况下,您不希望每个列和select(*)会导致数据库服务器做不必要的工作,并且必须通过网络传递不必要的信息。它不太可能造成明显的问题,除非系统被大量使用或网络连接很慢。

引用自这篇文章。

永远不要用“SELECT *”,

我发现使用“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 *工作良好的情况下,数据结构受到严格控制(它是零售软件),因此没有太大的风险,有人会将一个巨大的BLOB字段发送到表中。