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

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


当前回答

If you name the columns in a SELECT statement, they will be returned in the order specified, and may thus safely be referenced by numerical index. If you use "SELECT *", you may end up receiving the columns in arbitrary sequence, and thus can only safely use the columns by name. Unless you know in advance what you'll be wanting to do with any new column that gets added to the database, the most probable correct action is to ignore it. If you're going to be ignoring any new columns that get added to the database, there is no benefit whatsoever to retrieving them.

其他回答

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

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

使用列名进行选择提高了数据库引擎从索引访问数据的可能性,而不是查询表数据。

当数据库模式发生变化时,SELECT *使您的系统暴露在意想不到的性能和功能变化中,因为您要将任何新列添加到表中,即使您的代码还没有准备好使用或显示这些新数据。

在很多情况下,SELECT *会在应用程序的运行时导致错误,而不是在设计时。它隐藏了应用程序中列更改或坏引用的信息。

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

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

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

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

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

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