为什么SELECT*被认为有害?

The*_*ith 241 sql database

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

我知道这SELECT COUNT(*)是一些DB上的性能问题,但是如果你真的想要每一列呢?

Dav*_*kle 297

实际上有三个主要原因:

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

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

  • 约束问题.SELECT*时,可以从两个不同的表中检索两个同名的列.这通常会使您的数据使用者崩溃.想象一下连接两个表的查询,这两个表都包含一个名为"ID"的列.消费者如何知道哪个是哪个?当底层表结构发生更改时,SELECT*还会混淆视图(至少在某些版本的SQL Server中) - 视图不会重建,而返回的数据可能是无意义的.而最糟糕的部分是,您可以随心所欲地为您的列命名,但是下一个出现的人可能无法知道他必须担心添加会与您已开发的列发生冲突的列名.

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

  • 临时查询. 在尝试调试某些东西时,特别是在我可能不熟悉的狭窄桌子上,SELECT*通常是我最好的朋友.它帮助我看到正在发生的事情,而无需对底层列名称进行大量研究.列名称越长,这就越大"加".

  • 当*表示"一行"时. 在下面的使用案例中,SELECT*就好了,并且传言它是性能杀手只是城市传说,这可能在很多年前有一定的效力,但现在还没有:

    SELECT COUNT(*) FROM table;
    
    Run Code Online (Sandbox Code Playgroud)

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

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

    SELECT a.ID FROM TableA a
    WHERE EXISTS (
        SELECT *
        FROM TableB b
        WHERE b.ID = a.B_ID);
    
    Run Code Online (Sandbox Code Playgroud)

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

  • 使用"SELECT id,name"与"SELECT*"一样,在使用连接时从两个不同的表中选择两个同名的列.使用表名进行前缀可以解决两种情况下的问题. (15认同)
  • 就引用而言,您可以检查查询计划 - 如果子查询中有"*"而选择列时,它们是相同的.它们是相同的,因为基于成本的优化器"识别"在语义上,你在谈论满足标准的任何行 - 它不是硬件或速度的问题. (6认同)
  • 使用`*`的另一个好处是,在某些情况下,它可以更好地利用MySQL的缓存系统.如果您正在运行大量类似的`select`查询请求不同的列名(`选择A其中X`,`选择B其中X',...)使用`select*其中X`将允许缓存处理大量的查询,这可以带来显着的性能提升.这是一个特定于应用程序的场景,但值得记住. (4认同)
  • @rgasiore:至于你的第一点,也许你适合一个小型应用程序,你是团队中唯一的开发人员,并且它没有长寿 - 一个一次性应用程序。但是,如果您在一个团队中工作,或者您的数据库可能会发生任何变化,那么使用 SELECT * 就是错误的举动。仅仅因为您*今天*消耗了所有列并不意味着您*明天*也会这样做。就您的第二点而言,您可能需要阅读我的 SQL Server 示例,并重新阅读我的文章并进行一些思考。 (3认同)
  • 8年后,但想补充一点未提及的歧义。处理数据库中的200多个表并混合使用命名约定。在查看与查询结果交互的代码时,“ SELECT *”会迫使开发人员查看所涉及的表模式,以确定受影响/可用的列,例如在“ foreach”或“ serialize”中。反复查看架构以跟踪发生的事情的任务将不可避免地增加调试和开发相关代码所涉及的总时间。 (2认同)

OMG*_*ies 88

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

性能

*速记速度可能很慢,因为:

  • 并非所有字段都已编制索引,从而强制执行全表扫描 - 效率较低
  • 保存以通过网络发送SELECT *的内容可能会导致全表扫描
  • 返回的数据超出了所需数量
  • 使用可变长度数据类型返回尾随列可能导致搜索开销

保养

使用时SELECT *:

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

设计

SELECT *是一种反模式:

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

应该何时使用"SELECT*"?

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

否则,显式列出要在查询中使用的每一列 - 最好是在使用表别名时.


Mar*_*ers 18

即使您现在想要选择每一列,也可能不希望在某人添加一个或多个新列后选择每一列.如果你写与查询SELECT *你正在服用的,在某些时候有人可能会增加一列文本,让您的查询的运行速度比较慢,即使你实际上并不需要该列中的风险.

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

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

  • 特别是如果新列是一个3兆字节的BLOB (21认同)
  • 性能是一个方面,但通常还有一个正确的方面:用`*`投影的结果的形状可能意外地*改变*并且这可能在应用程序本身中造成严重破坏:由序数引用的列(例如sqldatareader.getstring( 2))突然检索一个*不同的*列,任何`INSERT ... SELECT*`都会中断,依此类推. (5认同)
  • @Matti - 但希望他们会考虑更多的思考*"嘿,让一个巨大的BLOB列放到这张桌子上!"*.*(是的,傻瓜希望我知道,但一个人不能梦想?)* (2认同)
  • @chaos:把blob放在桌子上并不会真的伤害你的表现......除非你使用SELECT*... ;-) (2认同)
  • 您不应该担心性能,直到它导致真正的问题。而且,`SELECT *` 不是保存几个字符的问题。这是一个节省数小时调试时间的问题,因为很容易忘记指定新添加的列。 (2认同)