Java程序員在寫SQL程序時(shí)候常犯的10個(gè)錯(cuò)誤
來源:易賢網(wǎng) 閱讀:693 次 日期:2014-09-05 12:01:08
溫馨提示:易賢網(wǎng)小編為您整理了“Java程序員在寫SQL程序時(shí)候常犯的10個(gè)錯(cuò)誤”,方便廣大網(wǎng)友查閱!

Java程序員編程時(shí)需要混合面向?qū)ο笏季S和一般命令式編程的方法,能否完美的將兩者結(jié)合起來完全得依靠編程人員的水準(zhǔn):

技能(任何人都能容易學(xué)會(huì)命令式編程)

模式(有些人用“模式-模式”,舉個(gè)例子,模式可以應(yīng)用到任何地方,而且都可以歸為某一類模式)

心境(首先,要寫個(gè)好的面向?qū)ο蟪绦蚴潜让钍匠绦螂y的多,你得花費(fèi)一些功夫)

但當(dāng)Java程序員寫SQL語句時(shí),一切都不一樣了。SQL是說明性語言而非面向?qū)ο蠡蚴敲钍骄幊陶Z言。在SQL中要寫個(gè)查詢語句是很簡單的。但在Java里類似的語句卻不容易,因?yàn)槌绦騿T不僅要反復(fù)考慮編程范式,而且也要考慮算法的問題。

下面是Java程序員在寫SQL時(shí)常犯的錯(cuò)誤(沒有特定的順序):

1.忘掉NULL

Java程序員寫SQL時(shí)對(duì)NULL的誤解可能是最大的錯(cuò)誤。也許是因?yàn)?并非唯一理由)NULL也稱作UNKNOWN。如果被稱作UNKNOWN,這還好理解些。另一個(gè)原因是,當(dāng)你從數(shù)據(jù)庫拿東西或是綁定變量時(shí),JDBC將SQL NULL 和Java中的null對(duì)應(yīng)了起來。這樣導(dǎo)致了NULL = NULL(SQL)和null=null(Java)的誤解。

對(duì)于NULL最大的誤解是當(dāng)NULL被用作行值表達(dá)式完整性約束條件時(shí)。

另一個(gè)誤解出現(xiàn)在對(duì)于NULL 在 NOT IN anti-joins的應(yīng)用中。

解決方法:

好好的訓(xùn)練你自己。當(dāng)你寫SQL時(shí)要不停得想到NULL的用法:

這個(gè)NULL完整性約束條件是正確的?

NULL是否影響到結(jié)果?

2.在Java內(nèi)存中處理數(shù)據(jù)

很少有Java開發(fā)者能將SQL理解的很好.偶爾使用的JOIN,還有古怪的UNION,好吧.但是對(duì)于窗口函數(shù)呢?還有對(duì)集合進(jìn)行分組呢?許多的Java開發(fā)者將SQL數(shù)據(jù)加載到內(nèi)存中,將這些數(shù)據(jù)轉(zhuǎn)換成某些相近的集合類型,然后再那些集合上面使用邊界循環(huán)控制結(jié)構(gòu)(至少在Java8的集合升級(jí)以前)執(zhí)行令人生厭的數(shù)學(xué)運(yùn)算.

但是一些SQL數(shù)據(jù)庫支持先進(jìn)的(而且是SQL 標(biāo)準(zhǔn)支持的!)OLAP特性,這一特性表現(xiàn)更好而且寫起來也更加方便.一個(gè)(并不怎么標(biāo)準(zhǔn)的)例子就是Oracle超棒的MODEL分句.只讓數(shù)據(jù)庫來做處理然后只把結(jié)果帶到Java內(nèi)存中吧.因?yàn)楫吘顾蟹浅B斆鞯募一镆呀?jīng)對(duì)這些昂貴的產(chǎn)品進(jìn)行了優(yōu)化.因此實(shí)際上,通過將OLAP移到數(shù)據(jù)庫,你將獲得一下兩項(xiàng)好處:

便利性.這比在Java中編寫正確的SQL可能更加的容易.

性能表現(xiàn).數(shù)據(jù)庫應(yīng)該比你的算法處理起來更加快.而且更加重要的是,你不必再去傳遞數(shù)百萬條記錄了.

完善的方法:

每次你使用Java實(shí)現(xiàn)一個(gè)以數(shù)據(jù)為中心的算法時(shí),問問自己:有沒有一種方法可以讓數(shù)據(jù)庫代替為我做這種麻煩事.

3. 使用UNION代替UNION ALL

太可恥了,和UNION相比UNION ALL還需要額外的關(guān)鍵字。如果SQL標(biāo)準(zhǔn)已經(jīng)規(guī)定了支持,那么可能會(huì)更好點(diǎn)。

UNION(允許重復(fù))

UNION DISTINCT (去除了重復(fù))

移除重復(fù)行不僅很少需要(有時(shí)甚至是錯(cuò)的),而且對(duì)于帶很多行的大數(shù)據(jù)集合會(huì)相當(dāng)慢,因?yàn)閮蓚€(gè)子select需要排序,而且每個(gè)元組也需要和它的子序列元組比較。

注意即使SQL標(biāo)準(zhǔn)規(guī)定了INTERSECT ALL和EXCEPT ALL,很少數(shù)據(jù)庫會(huì)實(shí)現(xiàn)這些沒用的集合操作符。

處理方法:

每次你寫UNION語句時(shí),考慮實(shí)際上是否需要UNION ALL語句。

4.通過JDBC分頁技術(shù)給大量的結(jié)果進(jìn)行分頁操作

大部分的數(shù)據(jù)庫都會(huì)支持一些分頁命令實(shí)現(xiàn)分頁效果,譬如LIMIT..OFFSET,TOP..START AT,OFFSET..FETCH語句等。即使沒有支持這些語句的數(shù)據(jù)庫,仍有可能對(duì)ROWNUM(甲骨文)或者是ROW NUMBER() OVER()過濾(DB2,SQL Server2008等),這些比在內(nèi)存中實(shí)現(xiàn)分頁更快速。在處理大量數(shù)據(jù)中,效果尤其明顯。

糾正:

僅僅使用這些語句,那么一個(gè)工具(例如JOOQ)就可以模擬這些語句的操作。

5.在java內(nèi)存中加入數(shù)據(jù)

從SQL的初期開始,當(dāng)在SQL中使用JOIN語句時(shí),一些開發(fā)者仍舊有不安的感覺。這是源自對(duì)加入JOIN后會(huì)變慢的固有恐懼。假如基于成本的優(yōu)化選擇去實(shí)現(xiàn)嵌套循環(huán),在創(chuàng)建一張連接表源前,可能加載所有的表在數(shù)據(jù)庫內(nèi)存中,這可能是真的。但是這事發(fā)生的概率太低了。通過合適的預(yù)測,約束和索引,合并連接和哈希連接的操作都是相當(dāng)?shù)目?。這完全是是關(guān)于正確元數(shù)據(jù)(在這里我不能夠引用Tom Kyte的太多)。而且,可能仍然有不少的Java開發(fā)人員加載兩張表通過分開查詢到一個(gè)映射中,并且在某種程度上把他們加到了內(nèi)存當(dāng)中。

糾正:

假如你在各個(gè)步驟中有從各種表的查詢操作,好好想想是否可以表達(dá)你的查詢操作在單條語句中。

6.在一個(gè)臨時(shí)的笛卡爾積集合中使用 DISTINCT 或 UNION 消除重復(fù)項(xiàng)

通過復(fù)雜的連接,人們可能會(huì)對(duì)SQL語句中扮演關(guān)鍵角色的所有關(guān)系失去概念。特別的,如果這涉及到多列外鍵關(guān)系的話,很有可能會(huì)忘記在JOIN .. ON子句中增加相關(guān)的判斷。這會(huì)導(dǎo)致重復(fù)的記錄,但或許只是在特殊的情況下。有些開發(fā)者因此可能選擇DISTINCT來消除這些重復(fù)記錄。從三個(gè)方面來說這是錯(cuò)誤的:

它(也許)解決了表面癥狀但并沒有解決問題。它也有可能無法解決極端情況下的癥狀。

對(duì)具有很多列的龐大的結(jié)果集合來說它很慢。DISTINCT要執(zhí)行ORDER BY操作來消除重復(fù)。

對(duì)龐大的笛卡爾積集合來說它很慢,還是需要加載很多的數(shù)據(jù)到內(nèi)存中。

解決方法:

根據(jù)經(jīng)驗(yàn),如果你獲得了不需要的重復(fù)記錄,還是檢查你的JOIN判斷吧??赡茉谀硞€(gè)地方有一個(gè)很難覺察的笛卡爾積集合。

7. 不使用MERGE語句

這并不是一個(gè)過失,但是可能是缺少知識(shí)或者對(duì)于強(qiáng)悍的MERGE語句信心不足。一些數(shù)據(jù)庫理解其它形式的更新插入(UPSERT)語句, 如 MYSQL的重復(fù)主鍵更新語句,但是MERGE在數(shù)據(jù)庫中確是很強(qiáng)大,很重要,以至于大肆擴(kuò)展SQL標(biāo)準(zhǔn),例如SQL SERVER。

解決之道:

如果你使用像聯(lián)合INSERT和UPDATE或者聯(lián)合SELECT .. FOR UPDATE然后在INSERT或UPDATE等更新插入時(shí),請三思。你完全可以使用一個(gè)更簡單的MERGE語句來遠(yuǎn)離冒險(xiǎn)競爭條件。

8. 使用聚合函數(shù)代替窗口函數(shù)(window functions)

在介紹窗口函數(shù)之前,在SQL中聚合數(shù)據(jù)意味著使用GROUP BY語句與聚合函數(shù)相映射。在很多情形下都工作得很好,如聚合數(shù)據(jù)需要濃縮常規(guī)數(shù)據(jù),那么就在join子查詢中使用group查詢。

但是在SQL:2003中定義了窗口函數(shù),這個(gè)在很多主流數(shù)據(jù)庫都實(shí)現(xiàn)了它。窗口函數(shù)能夠在結(jié)果集上聚合數(shù)據(jù),但是卻沒有分組。事實(shí)上,每個(gè)窗口函數(shù)都有自己的、獨(dú)立的PARTITION BY語句,這個(gè)工具對(duì)于顯示報(bào)告太TM好了。

使用窗口函數(shù):

使SQL更易讀(但在子查詢中沒有GROUP BY語句專業(yè))

提升性能,像關(guān)系數(shù)據(jù)庫管理系統(tǒng)能夠更容易優(yōu)化窗口函數(shù)

解決方法:

當(dāng)你在子查詢中使用GROUP BY語句時(shí),請?jiān)偃紤]是否可以使用窗口函數(shù)完成。

9. 使用內(nèi)存間接排序

SQL的ORDER BY語句支持很多類型的表達(dá)式,包括CASE語句,對(duì)于間接排序十分有用。你可能重來不會(huì)在Java內(nèi)存中排序數(shù)據(jù),因?yàn)槟銜?huì)想:

SQL排序很慢

SQL排序辦不到

處理方法:

如果你在內(nèi)存中排序任何SQL數(shù)據(jù),請?jiān)偃紤],是否不能在數(shù)據(jù)庫中排序。這對(duì)于數(shù)據(jù)庫分頁數(shù)據(jù)十分有用。

10. 一條一條的插入大量紀(jì)錄

JDBC ”懂“批處理(batch),你應(yīng)該不會(huì)忘了它。不要使用INSERT語句來一條一條的出入成千上萬的記錄,(因?yàn)?每次都會(huì)創(chuàng)建一個(gè)新的PreparedStatement對(duì)象。如果你的所有記錄都插入到同一個(gè)表時(shí),那么就創(chuàng)建一個(gè)帶有一條SQL語句以及附帶很多值集合的插入批處理語句。你可能需要在達(dá)到一定量的插入記錄后才提交來保證UNDO日志瘦小,這依賴于你的數(shù)據(jù)庫和數(shù)據(jù)庫設(shè)置。

處理方法:

總是使用批處理插入大量數(shù)據(jù)。

更多信息請查看IT技術(shù)專欄

更多信息請查看網(wǎng)絡(luò)編程
易賢網(wǎng)手機(jī)網(wǎng)站地址:Java程序員在寫SQL程序時(shí)候常犯的10個(gè)錯(cuò)誤
由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!

2025國考·省考課程試聽報(bào)名

  • 報(bào)班類型
  • 姓名
  • 手機(jī)號(hào)
  • 驗(yàn)證碼
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡要咨詢 | 簡要咨詢須知 | 加入群交流 | 手機(jī)站點(diǎn) | 投訴建議
工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
云南網(wǎng)警備案專用圖標(biāo)
聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
咨詢QQ:526150442(9:00—18:00)版權(quán)所有:易賢網(wǎng)
云南網(wǎng)警報(bào)警專用圖標(biāo)