【问题标题】:Can select * usage ever be justified?可以选择 * 使用是合理的吗?
【发布时间】:2011-04-07 20:08:11
【问题描述】:

我一直向我的开发人员宣传 SELECT * 是邪恶的,应该像瘟疫一样避免。

有没有可以证明的情况?

我不是在谈论 COUNT(*) - 大多数优化人员都能弄清楚。

编辑

我说的是生产代码。

我看到的这种不良做法的一个很好的例子是一个遗留的 asp 应用程序,它在存储过程中使用select *,并使用ADO 循环返回的记录,但按索引获取列。您可以想象在字段列表末尾以外的位置添加新字段时会发生什么。

【问题讨论】:

  • 需要提取所有字段的时候有什么问题?
  • “有什么问题” 你永远不知道你会得到什么。或者按什么顺序。在编程中,您通常需要可预测的结果。
  • 是的,也许有人可以解释为什么 select * 不好?我当然不是 DBA,但是当 * 也能正常工作时,写出一大串列名似乎毫无意义。
  • @annakata:我怀疑“你永远不应该写 SQL”这样的答案会得到铁杆 SQL 程序员的青睐;-)
  • @kemp,您正在用一分钟的额外工作换取所有用户的数小时性能下降,只是懒惰不列出这些列。即使列出所有列,每个 select * 都比列出列的 select 花费更长的时间。每个带有内部连接的 select * 返回数据,您显然不需要浪费服务器和网络资源。如果对每个查询都执行此操作,则会导致难以修复的巨大性能问题。在大多数数据库中,您无论如何都可以将它们拖过来,那有多难?

标签: sql select


【解决方案1】:

如果您想查找所有列并希望排序,您可以执行以下操作(至少如果您使用 MySQL):

SHOW COLUMNS FROM mytable FROM mydb;(1)

您可以查看所有字段的所有相关信息。您可以防止类型问题,并且可以确定所有列名。这个命令非常快,因为你只需要表的结构。从结果中,您将选择所有名称并构建如下字符串:

"select " + fieldNames[0] + ", fieldNames[1]" + ", fieldNames[2] from mytable". (2)

如果您不想运行两个单独的 MySQL 命令,因为 MySQL 命令很昂贵,您可以将 (1) 和 (2) 包含到存储过程中,该存储过程将结果作为 OUT 参数,这样您将只需调用一个存储过程,每个命令和数据生成都将在数据库服务器上发生。

【讨论】:

    【解决方案2】:

    我知道我参加聚会很晚了,但我会在我知道无论列名如何我总是想要所有列时使用 select *。这可能是一个相当边缘的案例,但在数据仓库中,我可能想从 3rd 方应用程序暂存整个表。我的标准过程是删除临时表并运行

    select * 
    into staging.aTable 
    from remotedb.dbo.aTable
    

    是的,如果远程表上的架构发生变化,下游依赖项可能会引发错误,但无论如何都会发生这种情况。

    【讨论】:

      【解决方案3】:

      我很高兴在审计触发器中使用 *

      在这种情况下,它实际上可以证明是一个好处,因为它将确保如果将其他列添加到基表中,它将引发错误,因此不会忘记在审计触发器和/或审计表结构中处理这个问题.

      (如dotjoe)我也很高兴在派生表和列表表达式中使用它。虽然我习惯性地反过来做。

      WITH t
           AS (SELECT *,
                      ROW_NUMBER() OVER (ORDER BY a) AS RN
               FROM   foo)
      SELECT a,
             b,
             c,
             RN
      FROM   t; 
      

      我最熟悉 SQL Server 并且至少优化器没有问题认识到只有列 a,b,c 是必需的,并且在内表表达式中使用 * 不会导致任何不必要的开销检索和丢弃不需要的列。

      原则上SELECT * 在视图中应该没问题,并且它是视图中应该避免的最终SELECT,但是在 SQL Server 中这可能会导致问题,因为它存储视图的列元数据当基础表更改时不会自动更新,并且使用 * 可能会导致混淆和不正确的结果,除非运行 sp_refreshview 来更新此元数据。

      【讨论】:

      • +1,当不使用触发器而是使用 OUTPUT 子句将数据发送到审计表时
      【解决方案4】:

      在使用 CTE 时,我将在生产环境中使用它。但是,在这种情况下,它并不是真正的select *,因为我已经在 CTE 中指定了列。我只是不想在最终选择中重新指定。

      with t as (
          select a, b, c from foo
      )
      
      select t.* from t;
      

      【讨论】:

      • DRY 方法的绝佳示例。不要重复自己。
      【解决方案5】:

      Select * 在生产代码中的任何时候都是合理的:

      • 这不是性能瓶颈
      • 开发时间很关键

      为什么每次向表中添加字段时,我都需要返回并担心更改相关存储过程的开销?

      为什么我还要考虑我是否选择了正确的领域,而绝大多数时候我想要大部分,而绝大多数时候我不想要,还有什么是瓶颈?

      如果我有特定的性能问题,我会回去修复它。否则,在我的环境中,这只是我可以不做的过早(且昂贵)的优化。


      编辑..在讨论之后,我想我会补充一下:

      ...人们没有做其他不受欢迎的事情,例如尝试访问列(i),无论如何这可能会在其他情况下中断:)

      【讨论】:

      • 因为您应该看到返回的内容以及您现在是否需要在存储过程中使用它。通常会添加不需要的字段,如果显示这些字段会使用户感到困惑。在进行更改时懒得进行必要的维护是一个借口而不是一个好习惯。
      • 我同意你的看法。在应用程序的某些部分,性能并不是特别重要,在这种情况下,Select * 非常感谢 - 列出这些字段会影响未来的开发时间和潜在的未来错误
      • 使用 select * 是维护的噩梦。去过也做过。不得不修复别人的蹩脚代码。改变你的架构?一切都可以打破。只是不要在生产代码中使用 select *。
      • 迷惑用户?这是什么废话。好像 SQL 中的 'select *' 暗示了任何关于将向用户显示的内容。听说过数据层与 UI 分离吗?似乎不是。而“select *”只有在假设哪些列以特定顺序出现时才会破坏其他内容。当然,如果基础数据发生变化,'columns(i)' 可能会中断 - 但如果您在选择中也指定列,它可能会中断,并且会发生变化。不过,如果您愿意,请坚持通常的宗教教条。
      【解决方案6】:

      phpmyadmin 的开发人员如何确保他们显示您的数据库表的所有字段?

      【讨论】:

        【解决方案7】:

        取决于生产软件的上下文。

        如果您正在为表管理工具编写一个简单的数据访问层,用户将在其中选择表并在网格中查看结果,那么 *SELECT ** 似乎很好。

        换句话说,如果您选择通过其他方式处理“字段选择”(例如在检索结果集后使用自动或用户指定的过滤器),那么看起来就可以了。

        另一方面,如果我们谈论的是具有业务规则、已定义架构等的某种企业软件......那么我同意 *SELECT ** 是个坏主意。

        编辑:哦,当源表是触发器或视图的存储过程时,“*SELECT **”应该没问题,因为您正在通过其他方式管理结果集(视图的定义或存储过程的结果集) .

        【讨论】:

          【解决方案8】:

          您的问题已经得到了许多答案,但您似乎忽略了所有不会重复您想听到的内容的内容。尽管如此,这是第三次(到目前为止):有时 没有瓶颈。有时性能远胜于良好。有时表是不断变化的,修改每个 SELECT 查询只是要管理更多可能的不一致。有时您必须按照不可能的时间表交付,这是您需要考虑的最后一件事。

          如果您生活在项目符号时代,请输入所有列名。但为什么要停在那里?在无模式 dbms 中重新编写您的应用程序。见鬼,用汇编写你的自己的 dbms。那真的会向他们展示。

          【讨论】:

          • 你在摇摆不定,SM。他们都更喜欢坚持流行的宗教,而不是注意到实际上,一种尺寸并不适合所有人。令我惊讶的是,没有人提倡使用自动化工具(及其不可避免的成本)来重写所有通常不需要更改的 SQL。
          • 同意。这一切都取决于您的环境——对于大型项目,您希望优化所有内容。但是对于许多较小的项目(仍然可能是非常大的昂贵事务),'SELECT *' 很好。对于我的大多数项目,开发时间比性能更重要。任何体面的 ORM 都会解决索引/添加列问题。我会说当性能损失无关紧要时这是合理的,并且它不会导致错误/漏洞。某些级别的优化是好的,但并不总是必要的 - 或适当的。
          • 这里很明显:这与性能无关。 “SELECT *”是一个坏习惯,因为它会导致代码脆弱。
          【解决方案9】:

          我使用 select * 来查询针对读取优化的表(非规范化的平面数据)。非常有利,因为表格的目的只是为了支持应用程序中的各种视图。

          【讨论】:

            【解决方案10】:
            1. 我需要多次显示列名未知的表中的数据。所以我做了SELECT * 并在运行时获取了列名。

            2. 我收到了一个旧版应用程序,其中一个表有 200 列,一个视图有 300 列。SELECT * 的风险暴露不会比明确列出所有 300 列更糟糕。

              李>

            【讨论】:

            • 如果您在运行时不知道这些列,我会怀疑是设计问题。但确实,在这种情况下,这是唯一可行的选择。
            【解决方案11】:

            是的,但仅在意图实际从表中获取所有列而不是因为您想要表当前具有的所有列的情况下。

            例如,在我使用的一个系统中,我们有 UDF(用户定义字段),用户可以在其中选择他们想要的报告字段、顺序以及过滤。在构建结果集时,从我正在构建的临时表中简单地“选择 *”更有意义,而不必跟踪哪些列是活动的。

            【讨论】:

              【解决方案12】:

              我认为在exists 子句中使用select * 是合适的:

              select some_field from some_table 
              where exists 
               (select * from related_table [join condition...])
              

              在这种情况下,有些人喜欢使用select 1,但它并不优雅,也没有任何性能改进(早期优化再次出现)。

              【讨论】:

              • select 1 是早期优化而不优雅?世界卫生大会?!大声笑,这是1个字符或另一个字符之间的区别。几乎不需要任何努力。实际上,select 1 可能对存在子句有更好的理解。它只关心行。
              • @dotjoe:select 1 的问题在于人们认为他们正在创建智能优化(我听过很多次)。他们不是。至于概念上的区别,exists子句用于检查是否存在any行,而不仅仅是1,所以我认为*比1更好地描述any
              • 是的,我说过exists子句只关心行。那么,为什么我们需要使用*,它只与列有关?通过使用 1,您清楚地表明我们不关心列,只关心行。
              • 我对 1 的问题是“我是一个聪明的优化器”问题。有些人甚至使用select top 1 1(在SQL-Server 中)。至于概念问题,我认为我们永远无法达成协议。为了真正结束辩论,我们应该能够在存在子句中做的是完全省略列:select from table,或者使用另一种语法说“我不在乎”:select _ from table(从一些函数式语言中借来的)。当语言不能提供准确我们想要表达的工具时,概念上的差异都可以解释。
              • 确定答案:可以接受的。其他任何事情都是无知/迷信。 ANSI 标准提到不解析 EXIST 中的列列表。第 191 页,contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt。这导致了这个 sn-p: ...EXISTS(SELECT 1/0 FROM... 请在此处查看示例:stackoverflow.com/questions/2019958
              【解决方案13】:

              可以想象,您希望设计数据库和应用程序,以便可以向表中添加列,而无需重写应用程序。如果您的应用程序至少检查列名,它可以安全地使用SELECT * 并使用一些适当的默认操作处理其他列。当然,应用程序可以查询系统目录(或特定于应用程序的目录)以获取列信息,但在某些情况下,SELECT * 是这样做的语法糖。

              但是,这样做存在明显的风险,向应用程序添加所需的逻辑以使其可靠可能只是意味着在不太合适的介质中复制数据库的查询检查。我不会去推测现实生活中成本和收益是如何权衡的。

              在实践中,我坚持SELECT * 3 个案例(其他答案中提到了一些:

              • 作为临时查询,在 SQL GUI 或命令行中输入。
              • 作为EXISTS 谓词的内容。
              • 在处理通用表而不需要知道它们的含义的应用程序中(例如,转储程序或不同)。

              【讨论】:

                【解决方案14】:

                请记住,如果您使用 select * 并且您有一个联接,则至少一个字段将被发送两次(联接字段)。这无端浪费数据库资源和网络资源。

                【讨论】:

                  【解决方案15】:

                  我能想到的唯一一件事就是开发一个实用程序或 SQL 工具应用程序,该应用程序被编写为针对任何数据库运行。即使在这里,我也倾向于查询系统表以获取表结构,然后从中构建任何必要的查询。

                  我的团队最近在一个地方使用了SELECT *,我认为这没问题...我们有一个数据库作为另一个数据库的外观存在(称为 DB_Data),因此它主要由针对其他数据库中的表的视图。当我们生成视图时,我们实际上生成了列列表,但是 DB_Data 数据库中有一组视图是在将行添加到通用查找表时自动生成的(这个设计在我到达这里之前就已经到位)。我们编写了一个 DDL 触发器,以便当此进程在 DB_Data 中创建视图时,会在外观中自动创建另一个视图。由于生成的视图始终与 DB_Data 中的视图完全匹配,并且始终刷新并保持同步,为了简单起见,我们只使用了SELECT *

                  如果大多数开发人员在整个职业生涯中都没有在生产代码中合法使用SELECT *,我不会感到惊讶。

                  【讨论】:

                    【解决方案16】:

                    如果您在谈论实时代码,我想不出。

                    人们说它使添加列更容易开发(因此它们会自动返回并且可以在不更改存储过程的情况下使用),但他们不知道编写最佳代码/sql。

                    我只在编写不会被重用的临时查询时使用它(找出表的结构,当我不确定列名是什么时获取一些数据)。

                    【讨论】:

                      【解决方案17】:

                      当创建一个处理数据库的应用程序时,比如 phpmyadmin,并且你在一个显示完整表格的页面中,在这种情况下使用SELECT * 是合理的,我猜。

                      【讨论】:

                      • 不,在这种情况下不能。 Select * 仍然是一个性能问题,并且出于管理目的将列添加到表中,用户不应像 GUID 一样看到这些列。
                      • 我理解,但是在 phpmyadmin 性质的应用程序中,用户是数据库管理员,可以构建并允许查看列名,在这种情况下,我认为只使用 @987654322 是合理的@,我认为它不会比使用 all 字段名构建查询并执行它慢。
                      • +1,亚伦。前几天我正在使用有这个问题的数据库编辑器。每当在编辑器之外更改列时,我必须手动刷新元数据以获取表视图以显示新列。如果编辑器在查询时只是做了SELECT *,我就不用担心缓存的元数据会过期。
                      【解决方案18】:

                      作为一种工具,我使用它来快速刷新我的记忆,以了解我可以从查询中得到什么。作为生产级查询本身..没办法。

                      【讨论】:

                        【解决方案19】:

                        在许多情况下 SELECT * 是最佳解决方案。在 Management Studio 中运行临时查询只是为了了解您正在使用的数据。查询您还不知道列名的表,因为这是您第一次使用新模式。构建一次性的 quick'n'dirty 工具来进行一次性迁移或数据导出。

                        我同意,在“正确”开发中,您应该避免它 - 但在很多情况下,“正确”开发不一定是解决业务问题的最佳解决方案。只要您知道何时打破它们,规则和最佳实践就很棒。 :)

                        【讨论】:

                        • “查询你还不知道列名的表,因为这是你第一次使用新模式” - 例如当实现从 DB 模式构建其对象的 ORM 时,而不是相反。
                        • “规则和最佳实践很棒,只要你知道什么时候打破它们”+1,我总是被告知“这已经足够好了”。
                        • 不幸的是,根据我的经验,这不是人们使用 select * 的原因——他们使用它是因为懒惰。
                        【解决方案20】:

                        在生产代码中,我倾向于 100% 同意你。

                        但是,我认为在执行临时查询时,* 不仅仅证明了它的存在。

                        【讨论】:

                          猜你喜欢
                          • 2011-01-02
                          • 1970-01-01
                          • 1970-01-01
                          • 2011-07-29
                          • 2010-10-09
                          • 2019-03-29
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多