今天我就来和大家聊聊Java开发人员在编写SQL语句时会犯的一些常见错误,可能很多人都没有很好的理解。为了让大家更好的了解,边肖为大家总结了以下内容,希望大家能从这篇文章中有所收获。
Java开发人员在面向对象编程思维和命令行编程思维之间的协调取决于他们的能力,如下所示:
技巧(任何人都可以写命令行代码)主义(有些人使用“模式-模式”方法,即模式无处不在,用名字标记)情感(一开始,真正面向对象形式的代码比命令式代码更难理解。)
然而,当Java开发人员编写SQL语句时,一切都变得不同了。SQL是一种解释性语言,与面向对象思维和命令式思维无关。在SQL语言中,查询非常容易表达。但是用最好或者最正确的方式来写并不那么容易。开发者不仅需要重新思考自己的编程模式,还需要从集合论的角度进行深入思考。
以下是当Java开发人员让JDBC或jOOQ编写SQL语句时的几种常见的错误:
1.忘记了NULL
误解NULL的含义可能是Java开发人员在编写SQL时最常犯的错误。这可能是因为NULL也被称为UNKNOWN,但还有其他原因。当然,如果只叫UNKNOWN就更容易理解了。另一个原因是,当JDBC获取数据或绑定变量时,SQL中的null在Java中被映射为NULL。这可能会让人认为SQL中存在NULL=NULL,类似于Java中的null=null。
误解空值的一个更奇怪的例子是在行值表达式中使用空谓词。
另一个微妙的问题来自于对NOTIn反联接中NULL含义的误解。
解决办法
继续训练自己。始终清楚空值的含义。每次编写SQL时,请考虑:
谓词对空值正确吗?NULL会影响这个函数的结果吗?2.在Java内存中处理数据
一些Java开发人员对SQL特性有很好的理解。偶尔JOIN,散UNION,没问题。但是如果遇到窗口函数、结果集分组等等呢?许多Java开发人员会将SQL数据加载到内存中,将数据转换为一些合适的集合类型,并在非常长的循环结构中对集合执行烦人的数学运算(至少在Java 8改进容器之前是这样的)。
但是,一些SQL数据库不仅支持SQL标准,还支持高级OLAP功能,这些功能更高效、更易于编写。甲骨文的MODEL子句就是一个不标准的例子。只需让数据库处理数据,并将最终获得的结果加载到Java内存中。因为一些非常聪明的人优化了这些昂贵的产品。因此,事实上,通过迁移到OLAP数据库,您将获得两个好处:
简洁。用SQL编写正确的代码可能比用Java更容易。数据库可能会比你的算法更快。更重要的是,你不必通过网络传输数百万条记录。解决办法
每次用Java实现一个以数据为中心的算法,试着问问自己:有没有什么办法让数据库执行这些任务,只把结果交给我?
3.尽量使用UNION,而不是UNION ALL
与UNION相比,UNION ALL需要额外的关键词,相形见绌。如果在SQL标准中定义了以下支持,那就更好了:
UNION distinct一般很少需要删除重复项(有时删除重复项甚至是错误的),对于包含很多列的大型结果集来说,通常速度会很慢,因为这两个子查询需要排序,每个元组都需要与后面的元组进行比较。
需要注意的是,即使SQL标准指定了INTERSECTALL和EXCEPTALL,但几乎没有数据库实现这些无用的操作。
解决办法
每次你写UNION的时候,考虑一下你是否真的想写UNIONALL。
4.使用JDBC分页功能将大量结果分页
大多数数据库都支持通过LIMIT这样的子句对结果进行分页.偏移,顶部.开始于,偏移.FETCH等。没有这些子句的支持,仍然有ROWNUM(Oracle)或ROW_NUMBER()OVER()(DB2、SQL Server 2008及更早版本),这比内存分页要快得多。这对于大型数据集来说更加明显。
解决办法
只要使用那些子句或者工具(比如jOOQ),就可以为你模拟上面的分页子句。
5.将Java内存中实现连接
从SQL开发的早期阶段开始,一些开发人员在面对SQL连接时仍然感到不安。一直有一种与生俱来的恐惧——慢连接。如果基于成本的优化器选择在创建连接表源之前执行嵌套循环并将完整的表加载到数据库内存中,速度确实非常慢。但这种情况很少发生。有了适当的谓词、约束和索引,MERGEJOIN和HASHJOIN操作非常快。这与正确的元数据有关(我不需要再举Tom Kyte的例子)。然而
也有仍然可能有不少Java开发人要会从单独的查询中加载两个表到map容器中,在java内存中以某种方式进行连接操作。
解决办法
如果你从多个步骤的多个表中进行了SELECT操作,那要慎重考虑一下是否可以在一条语句中表达你所需要的查询功能。6.使用DISTINCT或UNION从一个笛卡尔积中删除重复
冗长连接的存在,会导致SQL语句中起作用的关系显得十分松散。具体地,如果涉及到多列外键关系,很有可能忘记在JOINON子句上添加谓词。这可能会导致重复的记录,但也许只在特殊情况下。然后一些开发者可能会选择使用DISTINCT再次删除这些重复记录。这种错误有三种危害:
可能治标不治本。甚至在某些边缘情况下,标都治不了这在有很多列的大结果集上会十分的缓慢。DISTINCT会执行ORDER BY操作来删除重复。这在大型笛卡尔积中也十分的缓慢,因为这样做仍然会导致在内存中加载大量数据。解决办法
作为一个经验法则,当你得到不想要的重复结果时,应该首先检查你的连接谓词。因为有可能是在某个地方存在着一个不易察觉的笛卡尔积。
7.不使用MERGE语句
严格意义上讲,这不是一个真正的错误,可能只是对于功能强大的MERGE语句缺乏足够的认知或存在着某种恐惧而已。有些数据库包括其他形式的UPSERT 语句,如MySQL的ONDUPLICATE KEY UPDATE子句。但MERGE真的十分强大,最重要的是在数据库中,它在很大程度上扩展了SQL标准,如SQL Server。
解决办法
如果你通过链接INSERT和UPDATE或链接SELECT... FOR UPDATE来实现UPSERTING,那么你要多想一想。抛开与运行条件的风险,你也许可以使用一个简单的MERGE语句来达到目的。
8.使用了聚合函数,而不是窗体功能
引入窗函数之前,使用GROUPBY子句与投影聚合函数是汇总数据的唯一方式。这在大部分情况下都十分有效,如果聚集后的数据需要由常规的数据进行补充,该分组的查询可以置于连接子查询中。
但是,SQL:2003定义了窗口功能,目前很多主流的数据库厂商也纷纷实现了窗口功能。窗口功能可以聚集结果集中未被分组的数据。事实上,每个窗口的功能支持自身独立的PARTITIONBY子句,这对于报表类应用是一个非常有用的工具。
使用窗口功能将:
导致更多的可读性SQL(减少子查询中非专用GROUP BY子句的存在)提高性能,作为一个RDBMS很可能更容易优化其窗口功能。解决办法
当你在一个子查询写一个GROUPBY子句时,仔细想想这是否能用一个窗口函数来完成。
9.使用内存排序法进行间接排序
在SQLORDER BY子句支持多种类型的表达式,包括CASE语句,这对间接排序非常有用。你应该永远可能在Java内存中对数据进行排序,因为你认为:
SQL排序太慢SQL排序不能做到这一点解决办法
如果你在内存中对任何SQL数据进行排序,请仔细想想,你是否能把排序迁移至数据库中。这和将分页迁移至数据库中的原因一样。
10 一个接一个的插入大量的记录
JDBC包含了批处理,而且你应该使用它。面对成千上万的记录,切勿为每一条记录都创建一个新的PreparedStatement来进行插入操作。如果你要将所有记录都插入到同一个表,使用单一的SQL语句和多个绑定值集合建立一个批处理的INSERT语句。根据您的数据库和数据库配置,您可能需要在一定数量的插入的记录后进行提交,为了保持UNDO日志不过分庞大。
解决办法
始终批量插入大型数据集。
Java开发者编写SQL语句时常见的10种错误,大家是不是有了大概了解,希望在编写的过程中一定要特别注意!
看完上述内容,你们对Java开发者编写SQL语句时常见错误分别有哪些有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/123895.html