为什么SELECT *被认为有害?

时间:2010-09-03 22:02:25

标签: sql database

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

我理解SELECT COUNT(*)是某些数据库的性能问题,但是如果你真的想要每一列呢?

16 个答案:

答案 0 :(得分:287)

实际上有三个主要原因:

  • 将数据移动到使用者身份的效率低。当您选择SELECT *时,您经常从数据库中检索比您的应用程序真正需要的功能更多的列。这会导致更多数据从数据库服务器移动到客户端,从而减慢访问速度并增加计算机负载,并且需要更多时间在网络上传输。当有人向基础表添加新列时,尤其如此,当原始使用者对其数据访问进行编码时,这些列不存在且不需要。

  • 索引问题。考虑一种您希望将查询调整为高性能的方案。如果您要使用*,并且它返回的列数超过您实际需要的数量,那么服务器通常必须执行更昂贵的方法来检索您的数据。例如,你将无法创建一个仅覆盖SELECT列表中的列的索引,即使你做了(包括所有列[ shudder ]),下一个来到这里的人并且在基础表中添加了一列会导致优化器忽略您的优化覆盖索引,并且您可能会发现查询的性能会因为没有明显原因而显着下降。

  • 绑定问题。当您选择SELECT *时,可以从两个不同的表中检索两个同名的列。这通常会使您的数据使用者崩溃。想象一下连接两个表的查询,这两个表都包含一个名为“ID”的列。消费者如何知道哪个是哪个?当基础表结构发生变化时,SELECT *还会混淆视图(至少在某些版本的SQL Server中) - the view is not rebuilt, and the data which comes back can be nonsense。而最糟糕的部分是,您可以随心所欲地为您的列命名,但是下一个出现的人可能无法知道他必须担心添加会与您已开发的列发生冲突的列名。

但对于SELECT *来说并不是一件坏事。我在这些用例中大量使用它:

  • 即席查询。在尝试调试某些内容时,尤其是在我可能不熟悉的狭窄表格中,SELECT *通常是我最好的朋友。它帮助我看到正在发生的事情,而无需对底层列名称进行大量研究。列名称越长,这就越大“加”。

  • 当*表示“一行”时。在以下用例中,SELECT *就好了,并且传言它是性能杀手只是城市传说可能有一定的效力许多年前,但现在不要:

    SELECT COUNT(*) FROM table;
    

    在这种情况下,*表示“计算行数”。如果您使用列名而不是*,,它将计算该列的值不为空的行。对我来说,COUNT(*)确实驱动了你计算的概念,并且避免了由于从聚合中消除NULL而导致的奇怪边缘情况。

    这种类型的查询也是如此:

    SELECT a.ID FROM TableA a
    WHERE EXISTS (
        SELECT *
        FROM TableB b
        WHERE b.ID = a.B_ID);
    

    在任何值得盐的数据库中,*只是意味着“一排”。你在子查询中放入什么并不重要。有些人在SELECT列表中使用b的ID,或者他们将使用数字1,但IMO这些约定几乎是荒谬的。你的意思是“计算行”,这就是*表示的意思。大多数查询优化器都足够聪明,可以知道这一点。 (虽然说实话,我只是知道这对SQL Server和Oracle来说是正确的。)

答案 1 :(得分:85)

SELECT语句中的星号字符“*”是查询中涉及的表中所有列的简写。

效果

*速记可能会更慢,因为:

  • 并非所有字段都已编入索引,强制进行全表扫描 - 效率低下
  • 通过电汇发送SELECT *所节省的费用可能导致全表扫描
  • 返回的数据超出了需要
  • 使用可变长度数据类型返回尾随列可能会导致搜索开销

维护

使用SELECT *时:

  • 不熟悉代码库的人将被迫查阅文档以了解在能够进行有效更改之前返回的列。使代码更具可读性,最大限度地减少不熟悉代码的人所需的模糊性和工作,从长远来看可以节省更多的时间和精力。
  • 如果代码取决于列顺序,SELECT *将隐藏在表更改了列顺序时等待发生的错误。
  • 即使您在撰写查询时需要每一列,以后可能也不会如此
  • 使用情况使分析变得复杂

设计

SELECT * 反模式

  • 查询的目的不太明显;应用程序使用的列是不透明的
  • 它尽可能打破了使用严格打字的模块化规则。明确几乎普遍更好。

何时应该使用“SELECT *”?

当显式需要所涉及的表中的每一列时,使用SELECT *是可以接受的,而不是在编写查询时存在的每一列。数据库将在内部将*扩展为完整的列列表 - 没有性能差异。

否则,明确列出要在查询中使用的每一列 - 最好是在使用表别名时。

答案 2 :(得分:18)

即使您现在想要选择每一列,您也可能不希望在某人添加一个或多个新列后选择每一列。如果您使用SELECT *编写查询,则会冒一些风险,即某人可能会添加一列文本,这会使您的查询运行速度更慢,即使您实际上并不需要该列。

  

如果您添加了想要的新列,是不是意味着要更改的代码更少?

如果您确实想要使用新列,则可能无论如何都必须对代码进行大量其他更改。您只保存, new_column - 只需输入几个字符。

答案 3 :(得分:4)

如果在SELECT语句中命名列,它们将按指定的顺序返回,因此可以通过数字索引安全地引用。如果使用“SELECT *”,则最终可能会以任意顺序接收列,因此只能按名称安全地使用列。除非您事先知道要添加到数据库的任何新列您想要做什么,否则最可能的正确操作是忽略它。如果您要忽略任何添加到数据库中的新列,则检索它们没有任何好处。

答案 4 :(得分:3)

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

答案 5 :(得分:3)

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

通常,您不希望每个列和select(*)都可能导致数据库服务器不必要的工作,并且必须通过网络传递不必要的信息。除非系统使用频繁或网络连接速度慢,否则不太可能引起明显的问题。

答案 6 :(得分:3)

如果您向表格添加字段,则会自动将其包含在您使用select *的所有查询中。这看起来很方便,但是当您获取的数据超出了您的需要时,它会使您的应用程序变慢,并且它实际上会在某些时候使您的应用程序崩溃。

您可以在结果的每一行中获取多少数据。如果向表中添加字段以使结果超过该限制,则在尝试运行查询时会收到错误消息。

这是一种很难找到的错误。你在一个地方做了一个改变,它在其他一些根本没有使用新数据的地方爆炸。它甚至可能是一个不太常用的查询,因此在有人使用它之前需要一段时间,这使得将错误连接到更改变得更加困难。

如果在结果中指定了所需的字段,则可以避免这种开销溢出。

答案 7 :(得分:3)

将其视为减少应用与数据库之间的耦合。

总结“代码味”方面:
SELECT *在应用和架构之间创建动态依赖关系。限制其使用是使依赖关系更加定义的一种方法,否则对数据库的更改更有可能导致应用程序崩溃。

答案 8 :(得分:2)

Reference taken from this article.

永远不要选择" SELECT *",

我发现只有一个理由使用" SELECT *"

如果您有特殊要求并在添加或删除列时按应用程序代码自动处理,则创建动态环境。在这种特殊情况下,您不需要更改应用程序和数据库代码,这将自动影响生产环境。在这种情况下,您可以使用“SELECT *”。

答案 9 :(得分:1)

通常,您必须将SELECT * ...的结果放入各种类型的数据结构中。如果没有指定结果到达的顺序,那么正确排列所有内容可能会很棘手(更容易错过更多模糊的字段)。

通过这种方式,您可以出于各种原因向表中添加字段(即使在它们的中间),而不会破坏整个应用程序的SQL访问代码。

答案 10 :(得分:1)

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

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

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

这也是同样的理由,如果你正在做INSERT,你应该总是指定列。

答案 11 :(得分:1)

我认为这并不存在真正的一揽子规则。在许多情况下,我避免使用SELECT *,但我也使用了SELECT *非常有用的数据框架。

与所有事情一样,有益处和成本。我认为利益与成本等式的一部分就是对数据结构的控制程度。在SELECT *运行良好的情况下,数据结构受到严格控制(它是零售软件),因此没有太多风险让某人将巨大的BLOB字段悄悄地转移到表中。

答案 12 :(得分:0)

在设计架构之前了解您的要求(如果可能)。

了解数据,       1)索引       2)使用的存储类型,       3)供应商引擎或功能;即......缓存,内存中的功能       4)数据类型       5)表的大小       6)查询频率       7)如果共享资源,则相关工作负载       8)测试

A)要求会有所不同。如果硬件无法支持预期的工作负载,则应重新评估如何在工作负载中提供要求。关于表的添加列。如果数据库支持视图,则可以使用特定的命名列(与select'*'对比)创建特定数据的索引(?)视图。定期检查您的数据和架构,以确保您永远不会遇到“垃圾进入” - > “垃圾”综合症。

假设没有其他解决方案;您可以考虑以下因素。问题总有多种解决方案。

1)索引:select *将执行一个tablescan。根据各种因素,这可能涉及磁盘搜索和/或与其他查询的争用。如果表是多用途的,请确保所有查询都是高效的,并在目标时间以下执行。如果存在大量数据,并且未调整您的网络或其他资源;你需要考虑到这一点。数据库是一个共享环境。

2)存储类型。即:如果您使用的是SSD,磁盘或内存。 I / O时间和系统/ CPU上的负载会有所不同。

3)DBA可以调整数据库/表以获得更高的性能吗?无论出于何种原因,团队都决定选择'*'是问题的最佳解决方案;可以将DB或表加载到内存中。 (或者其他方法......也许响应的目的是以2-3秒的延迟作出响应?---广告起作用以赚取公司收入......)

4)从基线开始。了解您的数据类型以及如何呈现结果。较小的数据类型,字段数减少了结果集中返回的数据量。这使得资源可用于其他系统需求。系统资源通常有限制; “始终”工作低于这些限制,以确保稳定性和可预测的行为。

5)表/数据的大小。 select'*'在小表中很常见。它们通常适合内存,响应时间很快。再次....审查您的要求。计划特征蠕变;始终计划当前和未来可能的需求。

6)查询/查询的频率。注意系统上的其他工作负载。如果此查询每秒触发一次,则表格很小。结果集可以设计为保留在缓存/内存中。但是,如果查询是一个频繁的批处理过程,其中包含千兆字节/兆兆字节的数据......您可能最好投入额外的资源以确保其他工作负载不受影响。

7)相关工作量。了解资源的使用方式。网络/系统/数据库/表/应用程序是专用的还是共享的?谁是利益相关者?这是用于生产,开发还是质量保证?这是一个临时的“快速修复”。你测试了这个场景吗?你会惊讶于今天当前硬件上可能存在多少问题。 (是的,性能很快......但设计/性能仍然下降。)系统是否需要每秒执行10K查询,而每秒执行5-10次查询。数据库服务器是专用的,还是其他应用程序,监视共享资源上的执行。一些应用程序/语言; O / S将消耗100%的内存,导致各种症状/问题。

8)测试:测试你的理论,并尽可能多地了解。您选择的“*”问题可能很重要,或者您可能甚至不需要担心。

答案 13 :(得分:0)

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

SELECT *会在您的数据库架构发生更改时将系统暴露给意外的性能和功能更改,因为您要将任何新列添加到表中,即使您的代码不准备使用或呈现新的数据

答案 14 :(得分:0)

还有更实际的理由:钱。当您使用云数据库并且必须为处理的数据付费时,没有解释来读取您将立即丢弃的数据。

例如:BigQuery

  

查询定价

     

查询定价是指运行SQL命令和用户定义函数的成本。 BigQuery使用一个指标为查询收费:处理的字节数。

Control projection - Avoid SELECT *

  

最佳做法:控制投影 - 仅查询您需要的列。

     

投影是指查询读取的列数。投射多余的列会导致额外的(浪费的)I / O和实现(写入结果)。

     

使用SELECT *是查询数据最昂贵的方式。当您使用SELECT *时,BigQuery会对表中的每一列进行完整扫描。

答案 15 :(得分:0)

为什么不使用SELECT * from table的原因:-

  1. 不必要的I / O

  2. 增加网络流量

  3. 脆弱的视图

  4. 联接查询中的冲突

  5. 更多应用程序内存

  6. 复制数据时的风险

  7. 取决于列顺序

始终使用列名将始终在大型数据库中为您提供帮助。