Java开发人员在编写SQL语句代码行时常犯的10个错误)教条(有些人使用“模式-模式”方法,即模式无处不在,并由名称标识)情绪情况(在早期,一个真实的对象面向形式的代码会比命令式形式的代码更有效率)难以理解。)但是,当Java开发人员编写SQL语句时,一切都变得不一样了。SQL是一种声明式语言,与面向对象和命令式思维无关。在SQL语言中,查询非常容易表达。但以最好或最正确的方式写作也不是那么容易。开发者不仅需要重新思考自己的编程模型,还需要从集合论的角度进行深入思考。以下是Java开发人员在使用JDBC或jOOQ编写SQL语句时常犯的一些错误(排名不分先后)1.忘记NULL对NULL含义的误解可能是Java开发人员在编写SQL时最常犯的错误。这可能是因为NULL也被称为UNKNOWN,但也有其他原因。当然,直接叫UNKNOWN就更容易理解了。还有一个原因是JDBC在取数据或者绑定变量的时候,SQL中的NULL映射到Java中的null。这可能会让人想到,类似于Java中的null==null,SQL中也存在NULL=NULL。一个更奇怪的错误解释NULL的例子是在行值表达式中使用NULL谓词。另一个微妙的问题是误解了NOTIn反连接中NULL的含义。解决办法是不断训练自己。始终清楚NULL的含义。每次编写SQL时,请考虑:NULL的谓词是否正确?NULL会影响函数的结果吗?2.在Java内存中处理数据一些Java开发人员非常熟悉SQL的特性。偶尔JOIN,散UNION,没问题。但是如果遇到开窗函数、结果集分组等怎么办?许多Java开发人员会将SQL数据加载到内存中,将数据转换成一些合适的集合类型,然后在具有非常冗长的循环结构的集合上烦人地执行。数学运算(至少在Java8改进容器之前)。但是,一些SQL数据库除了支持SQL标准外,还支持高级的OLAP特性,执行效率更好,编写起来也更容易。一个非标准的例子是Oracle的MODEL子句。只需让数据库处理数据并将最终结果加载到Java内存中即可。因为一些非常聪明的人优化了这些昂贵的产品。所以,实际上,通过迁移到OLAP数据库,您将获得两个好处:简单性。与在Java中相比,它可能使在SQL中编写正确的性能代码相对容易。数据库可能比您的算法更快。更重要的是,您不再需要通过网络传输数百万条记录。解决方案每次用Java实现以数据为中心的算法时,试着问问自己:有没有一种方法可以让数据库完成工作并将结果提供给我?3.尝试使用UNION而不是UNIONALL与UNION相比,UNIONALL所需的额外关键字相形见绌。如果在SQL标准中定义了以下支持,那就更好了:UNION(允许重复)UNIONDISTINCT(删除重复)通常很少需要重复数据删除(有时重复数据删除甚至是错误的),对于包含许多列集的大结果,它往往很慢,因为两个子查询需要排序,并且每个元组需要与后续元组进行比较。应该注意的是,尽管SQL标准指定了INTERSECTALL和EXCEPTALL,但几乎没有任何数据库实现这些不太有用的操作。解决方案每次编写UNION时,请考虑您是否真正指的是UNIONALL。4.使用JDBC分页对大量结果进行分页大多数数据库以某种方式支持分页结果,如LIMIT..OFFSET,TOP..STARTAT,OFFSET..FETCH等。在不支持这些子句的情况下,但是还是ROWNUM(Oracle)或ROW_NUMBER()OVER()(DB2、SQLServer2008及更早版本),这比在内存中分页要快得多。这对于大型数据集来说更为明显。解决方案就是使用那些可以为您模拟上述分页子句的子句或像jOOQ这样的工具。5、在从Java内存实现开发SQL的初期,部分开发者在面对SQL连接时,仍有一种不安的感觉。人们一直担心JOIN速度慢。如果基于成本的优化器选择执行嵌套循环并在创建连接表源之前将整个表加载到数据库内存中,那么它真的很慢。但这很少发生。通过适当的谓词、约束和索引,MERGEJOIN和HASHJOIN操作非常快。这与正确的元数据有关(我不需要再举TomKyte的例子了)。但是,仍然有可能有相当多的Java开发人员会从单独的查询中将两个表加载到一个映射容器中,然后以某种方式在Java内存中将它们连接起来。解决方案如果您对多个表进行多步SELECT操作,请仔细考虑您是否可以在一条语句中表达您需要的查询功能。#p#6。使用DISTINCT或UNION从笛卡尔积中删除重复长连接的存在会导致在SQL语句中起作用的关系显得非常松散。具体来说,如果涉及到多列外键关系,很可能忘记在JOINON子句中添加谓词。这可能会导致重复记录,但可能只是在特殊情况下。那么有些开发者可能会选择使用DISTINCT再次删除这些重复的记录。这个错误有三个危害:治标不治本。即使在某些极端情况下,它也无法解决症状。这在具有许多列的大型结果集上可能会非常慢。DISTINCT将执行ORDERBY操作以删除重复项。这在大型笛卡尔积上也非常慢,因为这样做仍然会导致大量数据加载到内存中。解决方案根据经验,当您得到不需要的重复结果时,您应该首先检查您的连接谓词。因为某处可能存在难以察觉的笛卡尔积。7、不要使用MERGE语句严格来说,这并不是真正的错误,可能只是缺乏认识或者对强大的MERGE语句有某种恐惧。一些数据库包括其他形式的UPSERT语句,例如MySQL的ONDUPLICATEKEYUPDATE子句。但是MERGE确实很强大,最重要的是在数据库中,它在很大程度上扩展了SQL标准,比如SQLServer。解决方案如果您通过链接INSERT和UPDATE或链接SELECT...FORUPDATE来实现UPSERTING,那么您必须多考虑一点。撇开必须运行条件的风险不谈,您可能可以使用简单的MERGE语句来解决问题。8.使用聚合函数代替表单函数在引入窗口函数之前,使用GROUPBY子句和投影聚合函数是汇总数据的唯一方法。这在大多数情况下效果很好,如果聚合数据需要补充常规数据,分组查询可以放在连接子查询中。不过SQL:2003定义了窗口函数,很多主流数据库厂商也都实现了窗口函数。窗口函数可以聚合结果集中未分组的数据。事实上,每个窗口函数都支持自己独立的PARTITIONBY子句,这是一个非常有用的报告应用程序工具。使用窗口函数将:导致更具可读性的SQL(减少子查询中非专用GROUPBY子句的存在)提高性能,因为RDBMS更有可能优化其窗口函数。解决方案在子查询中编写GROUPBY子句时,请仔细考虑是否可以使用窗口函数来完成。9、使用内存排序方式进行间接排序SQLORDERBY子句支持多种类型的表达式,包括CASE语句,这对间接排序非常有用。你应该总是在Java内存中对数据进行排序,因为你认为:SQL排序太慢SQL排序无法做到这一点解决方案如果你在内存中对任何SQL数据进行排序,请仔细考虑,你是否可以将排序迁移到数据库中。这与将分页移动到数据库中的原因相同。10逐条插入大量记录JDBC包含批处理,您应该使用它。面对数以万计的记录,不要为每条记录创建一个新的PreparedStatement来插入。如果要将所有记录插入到同一个表中,请使用单个SQL语句和多个绑定值集创建一个批处理INSERT语句。根据您的数据库和数据库配置,您可能需要在插入一定数量的记录后提交,以防止UNDO日志变得太大。解决方案始终是批量插入大型数据集。
