可以select*用法有理吗?
我一直向我的开发者宣传SELECT *
是邪恶的,应该像瘟疫一样避免。
有没有可以certificate的理由?
我不是在谈论COUNT(*)
,大多数优化者都能find。
编辑
我在谈论生产代码。
我看到的一个很好的例子是一个传统的asp应用程序,它在存储过程中使用select *
,并使用ADO
遍历返回的logging,但是通过索引得到了列。 你可以想象当一个新字段被添加到字段列表的末尾以外的地方时发生了什么。
我很高兴在审计触发器中使用*
。
在这种情况下,它实际上可以certificate是一种好处,因为它可以确保如果将其他列添加到基表中,则会引发错误,因此不会忘记在审计触发器和/或审计表结构中处理这些错误。
(像dotjoe )我也很高兴在派生表和列表expression式中使用它。 尽pipe我习惯反其道而行之。
WITH t AS (SELECT *, ROW_NUMBER() OVER (ORDER BY a) AS RN FROM foo) SELECT a, b, c, RN FROM t;
我大多熟悉SQL Server,至less这个优化器没有任何问题,因为只有列a,b,c
将被需要,并且在内部表expression式中使用*
不会导致任何不必要的开销检索和丢弃不需要的列。
原则上, SELECT *
在视图中应该是正确的,并且它是视图中的最后一个SELECT
,但是在SQL Server中,这可能会导致问题,因为它存储视图的列元数据,底层表更改和使用*
可能导致混淆和不正确的结果,除非运行sp_refreshview
更新此元数据。
有许多情况下,SELECT *是最佳解决scheme。 在Management Studio中运行即席查询只是为了了解您正在使用的数据。 查询你不知道列名的表,因为这是你第一次使用新的模式。 构build一次性快速工具来执行一次性迁移或数据导出。
我同意,在“适当的”发展中,你应该避免它 – 但有很多情况下,“适当”的发展不一定是一个业务问题的最佳解决scheme。 规则和最佳实践是伟大的,只要你知道什么时候打破他们。 🙂
在与CTE合作时,我会在生产中使用它。 但是,在这种情况下,它不是真正的select *
,因为我已经在CTE中指定了列。 我只是不想在最后selectrespecify。
with t as ( select a, b, c from foo ) select t.* from t;
如果你正在谈论实时代码,那我想不出来。
人们说,它使添加列更容易开发(所以他们自动得到返回,可以使用,而无需更改存储过程)不知道写优化的代码/ SQL。
我只有在编写临时查询时才使用它,这些查询不会被重用(查找表的结构,当我不确定列名是什么时,获取一些数据)。
我认为在一个exists
子句中使用select *
是适当的:
select some_field from some_table where exists (select * from related_table [join condition...])
有些人喜欢在这种情况下使用select 1
,但是它不够优雅,并且不会购买任何性能改进(再次提前优化)。
在生产代码中,我倾向于同意100%。
但是,我认为在执行临时查询时,不仅仅是certificate它的存在。
对于你的问题,你已经得到了很多答案,但是你似乎正在抛弃一切并不是你想听到的东西。 不过,这是第三次(到目前为止)的时间:有时候没有瓶颈。 有时候性能比罚款要好。 有时这些表格是不断变化的,并且修改每个SELECT查询只是可能出现的不一致情况。 有时你必须按照不可能的时间表来交付,这是你需要考虑的最后一件事情。
如果你住在子弹时间,当然,input所有的列名称。 但为什么要停在那里? 在无模式DBMS中重新编写应用程序。 地狱,编写自己的 dbms。 那真的会performance出来。
并记住,如果您使用select *,并且您有连接,则至less有一个字段将被发送两次(连接字段)。 这无用地浪费了数据库资源和networking资源。
作为一个工具,我用它来快速刷新我的记忆,以便我可以从查询中得到什么。 作为一个生产水平查询本身..没办法。
当创build一个处理数据库的应用程序时,比如phpmyadmin,并且你正在一个显示一个完整表的页面中,在这种情况下,使用SELECT *
可以certificate是合理的。
关于我能想到的唯一的事情就是在开发一个正在编写的针对任何数据库运行的实用程序或SQL工具应用程序时。 即使在这里,我会倾向于查询系统表来获取表结构,然后从中build立任何必要的查询。
有一个最近的地方,我的团队使用SELECT *
,我认为它是好的…我们有一个数据库存在作为一个门面针对另一个数据库(称之为DB_Data),所以它主要是对表在其他数据库中。 当我们生成视图时,我们实际上会生成列列表,但是在DB_Data数据库中有一组视图,这些视图是在将行添加到通用查找表(这种devise在我到达此处之前就已经到位)时自动生成的。 我们编写了一个DDL触发器,这样当在这个过程中在DB_Data中创build一个视图的时候,另外一个视图就会自动创build在这个视图中。 由于视图总是生成为与DB_Data中的视图完全匹配,并且始终刷新并保持同步,所以我们只是简单地使用了SELECT *
。
如果大多数开发人员在生产代码中没有合法使用SELECT *
情况下,他们的职业生涯就不会感到惊讶。
我用select *来查询为读取而优化的表(非规格化,平坦的数据)。 由于表格的目的仅仅是为了支持应用中的各种观点,因此非常有利。
phpmyadmin
的开发人员还能确保他们显示数据库表的所有字段吗?
可以想象,你会想要devise你的数据库和应用程序,以便你可以添加一列到一个表,而不需要重写你的应用程序。 如果您的应用程序至less检查列名称,则可以安全地使用SELECT *
并使用一些适当的默认操作处理其他列。 当然,应用程序可以咨询系统目录(或特定应用程序目录)的列信息,但在某些情况下, SELECT *
是语法糖。
然而,有明显的风险,并且向应用程序添加所需的逻辑以使其可靠可以简单地意味着将数据库的查询检查复制到不太合适的介质中。 我不打算推测成本和收益在现实生活中如何交换。
在实践中,我坚持SELECT *
3例(有些在其他答案中提到:
- 作为即席查询,input到SQL GUI或命令行中。
- 作为
EXISTS
谓词的内容。 - 在处理通用表的应用程序中,不需要知道它们的含义(例如自卸车或不同)。
是的,但只有在意图是实际获取表中的所有列的情况下,不是因为您需要表中当前所有的列。
例如,在我工作的一个系统中,我们有UDF(用户定义的字段),用户可以在报告中select他们想要的字段,顺序以及过滤。 在构build结果集时,只需从我正在构build的临时表中“select*”,而不必跟踪哪些列处于活动状态就更有意义了。
-
我需要多次显示列名未知的表中的数据。 所以我做了
SELECT *
并在运行时获得了列名。 -
我被交给了一个传统的应用程序,其中一个表有200列,一个视图有300个。来自
SELECT *
的风险暴露不会比明确列出所有300列更糟糕。
取决于生产软件的背景。
如果你正在为表格pipe理工具编写一个简单的数据访问层,用户将在网格中select表格和查看结果,那么看起来* SELECT **是好的。
换句话说,如果你select通过其他方式来处理“select字段”(比如在检索结果集之后自动或用户指定的filter),那么看起来就好了。
另一方面,如果我们谈论某种企业软件与商业规则,定义架构等…然后我同意* SELECT **是一个坏主意。
编辑:哦,当源表是一个触发器或视图的存储过程,“* SELECT **”应该罚款,因为你通过其他手段(视图的定义或存储过程的结果集)pipe理结果集。
在生产代码中Select *
是有道理的任何时候:
- 这不是性能瓶颈
- 开发时间至关重要
为什么我需要返回的开销,不必担心更改相关的存储过程,每次我添加一个字段到表中?
为什么我甚至不得不考虑我是否select了正确的领域,无论如何我绝大多数时间都是我所需要的,绝大多数时间我都没有select,还有其他的东西是瓶颈?
如果我有一个特定的性能问题,那么我会回去修复这个问题。 否则,在我的环境中,这只是过早(而且昂贵)的优化,我可以不做。
编辑..按照讨论,我想我会添加到这个:
…人们还没有做其他不希望的事情,比如试图访问列(i),这可能会在其他情况下打破:)
我知道我很晚参加派对,但是我会使用select *,只要我知道我总是希望所有的列不pipe列名。 这可能是一个相当大的情况,但在数据仓库,我可能想从第三方应用程序的整个表。 我的标准过程是放下临时表并运行
select * into staging.aTable from remotedb.dbo.aTable
是的,如果远程表上的模式发生变化,那么下游依赖关系可能会引发错误,但不pipe怎么样都会发生。
如果你想find所有的列和想要订购,你可以做以下事情(至less如果你使用MySQL):
SHOW COLUMNS FROM mytable FROM mydb;
(1)
你可以看到你所有的领域的每一个相关的信息。 你可以防止types的问题,你可以肯定知道所有的列名。 这个命令很快,因为你只是要求表的结构。 从结果中你将select所有的名字,并build立一个像这样的string:
"select " + fieldNames[0] + ", fieldNames[1]" + ", fieldNames[2] from mytable". (2)
如果你不想运行两个单独的MySQL命令,因为一个MySQL命令是很昂贵的,你可以将(1)和(2)包含在一个存储过程中,这个存储过程的结果就是一个OUT参数,这样你就可以调用存储过程以及每个命令和数据生成都将在数据库服务器上发生。