【51CTO.com快译】如今,网上许多文章都已明确地指出:使用“SELECT * ”作为SQL查询方式是一种极其危险的代码书写习惯。开发人员应该尽量在自己的程序中避免出现此类查询,取而代之的应该是明确地指定要查询的列名。不过,大家可能只是“知其然,而不知其所以然”。在本文中,让我向各位初级开发人员详细解释,此类SQL查询***实践背后的具体原因。
首先,我们经常面对的客观情况是:在Oracle数据库中,许多SQL开发人员都是从接触“SELECT * from EMP”(EMP为表的名称)之类的查询语句,开始学习SQL语言的。因此,除非能够给出充分的理由,否则我们很难撼动他们使用此类便捷查询语句的习惯。
下面,我将根据自己在应用编程中的实际经验,向大家证明使用Select * from table进行SQL查询的“七宗罪”。
1. 不必要的I/O(输入/输出)
通过使用SELECT * ,您虽然可以获得一些完全可以被忽略的返回数据,但是该获取过程可并不是免费的。那些本来可能只需要从索引页面中读取的数据检索,如今您却不得不从各个页面中以全量的方式读取出来。显然,此举会导致数据库端白白浪费各种有限的I/O周期。
另外,该方式也可能会拖慢您的查询速度。如果您好奇并想探究数据库后台的查询执行过程,以及查询引擎是如何顺次处理查询语句的话,我建议您参考:Markus Winand的《SQL Performance Explained》(请参见http://www.amazon.com/Performance-Explained-Everything-Developers-about/dp/3950307826/?tag=javamysqlanta-20),以及Udemy的《The Complete SQL BootCamp》(请参见https://click.linksynergy.com/fs-bin/click?id=JVFxdTr9V80&subid=0&offerid=323058.1&type=10&tmpid=14538&RD_PARM1=https%3A%2F%2Fwww.udemy.com%2Fthe-complete-sql-bootcamp%2F)课程。
2. 增加的网络流量
SELECT * 虽然能返回比用户预期更多的数据,但是相应地,这些数据的传输势必会消耗更多的网络带宽资源。与此同时,网络带宽的增加也就意味着:那些真正为用户所需要的数据将会花费更长的时间,才能被传送到客户端的应用程序上。例如:如果您可能是在本地计算机上运行由SQL Server Management Studio(请详见http://bit.ly/2CXPyBB)、Toad或SQL Developer for Oracle(请参见http://bit.ly/2xQzAsd)提供的查询编辑器,或是在某个Java应用服务器上运行此类查询,这都会耗费您不少的网络流量与资源。
3.更多的应用内存
随着业务数据的猛增,您的应用程序可能需要使用更多的内存,来保存由此类查询方式所产生的,可能来自Microsoft SQL Server(请参见http://www.java67.com/2018/01/top-4-free-microsoft-sql-server-books.html)的各种无用数据。
4.产生依赖于列排序的结果集(ResultSet)
当您在应用程序中使用SELECT * 查询后,您会得到一些依赖于数据表的列排序的结果集。因此,一旦有新的列被添加,或者是列排序被修改了,它们都会对查询的结果集产生不同的影响。
5.新增列会破坏既有的视图
如果您在视图(请参见http://www.java67.com/2012/11/what-is-difference-between-view-vs-materialized-view-database-sql.html)中使用了SELECT * ,那么一旦有新的列被添加,同时旧的列从表中被去除时,您所构建的原有视图就会被破坏,进而返回给用户错误的结果。
为避免此类情况的发生,您应该始终在SQL Server数据库(请参见http://javarevisited.blogspot.sg/2013/11/difference-between-char-varchar-nchar-nvarchar-sql-database.html#axzz5CSnhvSWV)里,对于视图的定义中,包含WITH SCHEMABINDING选项。
6. 连接查询中的冲突
如果您在连接查询(JOIN Query,请参见https://javarevisited.blogspot.com/2012/11/how-to-join-three-tables-in-sql-query-mysql-sqlserver.html#axzz5az3hfsHW)中使用了SELECT * ,那么一旦在多个表中出现了具有相同名称的列,例如status、active和name等,就可能会产生各种并发式的冲突。
虽说在直接查询中,出现问题的可能性不大,但是当您试着按其中的某一列进行排序、或是在公用表表达式(Common Table Expression,CTE)、以及派生表(derived table)中使用查询的时候,您就需要进行各种进一步的调整,以避免产生冲突了。
7.在表间复制数据时的风险
您可能会经常使用“SELECT * into INSERT . . .”之类的语句,以实现将某些数据从一张表复制到另一张表。如果在两张表中,各个列的排列顺序略有不同,那么就可能会出现将不正确的数据复制到错误列中的情况。
一些程序员可能会认为:由于查询解析器必须额外地验证某些静态值,因此导致了在EXISTS语句(译者注:即检验查询的结果是否返回数据,请参见https://javarevisited.blogspot.com/2016/01/sql-exists-example-customers-who-never-ordered.html)中使用SELECT * 要比SELECT 1的速度更快一些。此观点搁在过去可能会有几分道理。但是现在,各种数据库解析器已经发展得相当智能了,它们判断EXISTS语句的效率,与产生SELECT结果列表(请参见https://javarevisited.blogspot.com/2016/04/how-to-convert-result-of-select-command-to-comma-separated-String-in-SQL-Server.html)将毫无关系。
结论
通过上述七点分析,相信您应该明白了为什么不能在SQL查询中滥用SELECT * 的原因吧?可见,您应该尽可能地在查询中,使用显式的列名称,而不是那些星号通配符。此举不但能够提高您的代码效率,也可以使您的程序更加清晰。与此同时,该方法还能够帮助您创建各种具有可维护性的代码。而且,如果后期在表中有新的一列被添加的话,您的代码也不会因此受到影响,您仍然会拥有来自原始数据表的参考视图。
原文标题:7 Reasons Why Using SELECT * FROM TABLE in SQL Query Is a Bad Idea,作者:Javin Paul
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】