時(shí)間:2015-06-28 00:00:00 來(lái)源:IT貓撲網(wǎng) 作者:網(wǎng)管聯(lián)盟 我要評(píng)論(0)
??? 避免 LEFT JOIN 和 NULL?
??? 當(dāng)然,有很多時(shí)候您需要執(zhí)行 LEFT JOIN 和使用 NULL 值。但是,它們并不適用于所有情況。改變 SQL 查詢(xún)的構(gòu)建方式可能會(huì)產(chǎn)生將一個(gè)花幾分鐘運(yùn)行的報(bào)告縮短到只花幾秒鐘這樣的天壤之別的效果。有時(shí),必須在查詢(xún)中調(diào)整數(shù)據(jù)的形態(tài),使之適應(yīng)應(yīng)用程序所要求的顯示方式。雖然 TABLE 數(shù)據(jù)類(lèi)型會(huì)減少大量占用資源的情況,但在查詢(xún)中還有許多區(qū)域可以進(jìn)行優(yōu)化。SQL 的一個(gè)有價(jià)值的常用功能是 LEFT JOIN。它可以用于檢索第一個(gè)表中的所有行、第二個(gè)表中所有匹配的行、以及第二個(gè)表中與第一個(gè)表不匹配的所有行。例如,如果希望返回每個(gè)客戶(hù)及其定單,使用 LEFT JOIN 則可以顯示有定單和沒(méi)有定單的客戶(hù)。
??? 此工具可能會(huì)被過(guò)度使用。LEFT JOIN 消耗的資源非常之多,因?yàn)樗鼈儼c NULL(不存在)數(shù)據(jù)匹配的數(shù)據(jù)。在某些情況下,這是不可避免的,但是代價(jià)可能非常高。LEFT JOIN 比 INNER JOIN 消耗資源更多,所以如果您可以重新編寫(xiě)查詢(xún)以使得該查詢(xún)不使用任何 LEFT JOIN,則會(huì)得到非??捎^(guān)的回報(bào)。
??? 加快使用 LEFT JOIN 的查詢(xún)速度的一項(xiàng)技術(shù)涉及創(chuàng)建一個(gè) TABLE 數(shù)據(jù)類(lèi)型,插入第一個(gè)表(LEFT JOIN 左側(cè)的表)中的所有行,然后使用第二個(gè)表中的值更新 TABLE 數(shù)據(jù)類(lèi)型。此技術(shù)是一個(gè)兩步的過(guò)程,但與標(biāo)準(zhǔn)的 LEFT JOIN 相比,可以節(jié)省大量時(shí)間。一個(gè)很好的規(guī)則是嘗試各種不同的技術(shù)并記錄每種技術(shù)所需的時(shí)間,直到獲得用于您的應(yīng)用程序的執(zhí)行性能最佳的查詢(xún)。
??? 測(cè)試查詢(xún)的速度時(shí),有必要多次運(yùn)行此查詢(xún),然后取一個(gè)平均值。因?yàn)椴樵?xún)(或存儲(chǔ)過(guò)程)可能會(huì)存儲(chǔ)在 SQL Server 內(nèi)存中的過(guò)程緩存中,因此第一次嘗試耗費(fèi)的時(shí)間好像稍長(zhǎng)一些,而所有后續(xù)嘗試耗費(fèi)的時(shí)間都較短。另外,運(yùn)行您的查詢(xún)時(shí),可能正在針對(duì)相同的表運(yùn)行其他查詢(xún)。當(dāng)其他查詢(xún)鎖定和解鎖這些表時(shí),可能會(huì)導(dǎo)致您的查詢(xún)要排隊(duì)等待。例如,如果您進(jìn)行查詢(xún)時(shí)某人正在更新此表中的數(shù)據(jù),則在更新提交時(shí)您的查詢(xún)可能需要耗費(fèi)更長(zhǎng)時(shí)間來(lái)執(zhí)行。
??? 避免使用 LEFT JOIN 時(shí)速度降低的最簡(jiǎn)單方法是盡可能多地圍繞它們?cè)O(shè)計(jì)數(shù)據(jù)庫(kù)。例如,假設(shè)某一產(chǎn)品可能具有類(lèi)別也可能沒(méi)有類(lèi)別。如果 Products 表存儲(chǔ)了其類(lèi)別的 ID,而沒(méi)有用于某個(gè)特定產(chǎn)品的類(lèi)別,則您可以在字段中存儲(chǔ) NULL 值。然后您必須執(zhí)行 LEFT JOIN 來(lái)獲取所有產(chǎn)品及其類(lèi)別。您可以創(chuàng)建一個(gè)值為"No Category"的類(lèi)別,從而指定外鍵關(guān)系不允許 NULL 值。通過(guò)執(zhí)行上述操作,現(xiàn)在您就可以使用 INNER JOIN 檢索所有產(chǎn)品及其類(lèi)別了。雖然這看起來(lái)好像是一個(gè)帶有多余數(shù)據(jù)的變通方法,但可能是一個(gè)很有價(jià)值的技術(shù),因?yàn)樗梢韵?SQL 批處理語(yǔ)句中消耗資源較多的 LEFT JOIN。在數(shù)據(jù)庫(kù)中全部使用此概念可以為您節(jié)省大量的處理時(shí)間。請(qǐng)記住,對(duì)于您的用戶(hù)而言,即使幾秒鐘的時(shí)間也非常重要,因?yàn)楫?dāng)您有許多用戶(hù)正在訪(fǎng)問(wèn)同一個(gè)聯(lián)機(jī)數(shù)據(jù)庫(kù)應(yīng)用程序時(shí),這幾秒鐘實(shí)際上的意義會(huì)非常重大。
??? 靈活使用笛卡爾乘積?
??? 對(duì)于此技巧,我將進(jìn)行非常詳細(xì)的介紹,并提倡在某些情況下使用笛卡爾乘積。出于某些原因,笛卡爾乘積 (CROSS JOIN) 遭到了很多譴責(zé),開(kāi)發(fā)人員通常會(huì)被警告根本就不要使用它們。在許多情況下,它們消耗的資源太多,從而無(wú)法高效使用。但是像 SQL 中的任何工具一樣,如果正確使用,它們也會(huì)很有價(jià)值。例如,如果您想運(yùn)行一個(gè)返回每月數(shù)據(jù)(即使某一特定月份客戶(hù)沒(méi)有定單也要返回)的查詢(xún),您就可以很方便地使用笛卡爾乘積。
??? 雖然這看起來(lái)好像沒(méi)什么神奇的,但是請(qǐng)考慮一下,如果您從客戶(hù)到定單(這些定單按月份進(jìn)行分組并對(duì)銷(xiāo)售額進(jìn)行小計(jì))進(jìn)行了標(biāo)準(zhǔn)的 INNER JOIN,則只會(huì)獲得客戶(hù)有定單的月份。因此,對(duì)于客戶(hù)未訂購(gòu)任何產(chǎn)品的月份,您不會(huì)獲得 0 值。如果您想為每個(gè)客戶(hù)都繪制一個(gè)圖,以顯示每個(gè)月和該月銷(xiāo)售額,則可能希望此圖包括月銷(xiāo)售額為 0 的月份,以便直觀(guān)標(biāo)識(shí)出這些月份。如果使用 Figure 2(最后一頁(yè)) 中的 SQL,數(shù)據(jù)則會(huì)跳過(guò)銷(xiāo)售額為 0 美元的月份,因?yàn)樵诙▎伪碇袑?duì)于零銷(xiāo)售額不會(huì)包含任何行(假設(shè)您只存儲(chǔ)發(fā)生的事件)。
??? Figure 3(最后一頁(yè))中的代碼雖然較長(zhǎng),但是可以達(dá)到獲取所有銷(xiāo)售數(shù)據(jù)(甚至包括沒(méi)有銷(xiāo)售額的月份)的目標(biāo)。首先,它會(huì)提取去年所有月份的列表,然后將它們放入第一個(gè) TABLE 數(shù)據(jù)類(lèi)型表 (@tblMonths) 中。下一步,此代碼會(huì)獲取在該時(shí)間段內(nèi)有銷(xiāo)售額的所有客戶(hù)公司的名稱(chēng)列表,然后將它們放入另一個(gè) TABLE 數(shù)據(jù)類(lèi)型表 (@tblCus-tomers) 中。這兩個(gè)表存儲(chǔ)了創(chuàng)建結(jié)果集所必需的所有基本數(shù)據(jù),但實(shí)際銷(xiāo)售數(shù)量除外。 第一個(gè)表中列出了所有月份(12 行),第二個(gè)表中列出了這個(gè)時(shí)間段內(nèi)有銷(xiāo)售額的所有客戶(hù)(對(duì)于我是 81 個(gè))。并非每個(gè)客戶(hù)在過(guò)去 12 個(gè)月中的每個(gè)月都購(gòu)買(mǎi)了產(chǎn)品,所以,執(zhí)行 INNER JOIN 或 LEFT JOIN 不會(huì)返回每個(gè)月的每個(gè)客戶(hù)。這些操作只會(huì)返回購(gòu)買(mǎi)產(chǎn)品的客戶(hù)和月份。
??? 笛卡爾乘積則可以返回所有月份的所有客戶(hù)。笛卡爾乘積基本上是將第一個(gè)表與第二個(gè)表相乘,生成一個(gè)行集合,其中包含第一個(gè)表中的行數(shù)與第二個(gè)表中的行數(shù)相乘的結(jié)果。因此,笛卡爾乘積會(huì)向表 @tblFinal 返回 972 行。最后的步驟是使用此日期范圍內(nèi)每個(gè)客戶(hù)的月銷(xiāo)售額總計(jì)更新 @tblFinal 表,以及選擇最終的行集。
??? 如果由于笛卡爾乘積占用的資源可能會(huì)很多,而不需要真正的笛卡爾乘積,則可以謹(jǐn)慎地使用 CROSS JOIN。例如,如果對(duì)產(chǎn)品和類(lèi)別執(zhí)行了 CROSS JOIN,然后使用 WHERE 子句、DISTINCT 或 GROUP BY 來(lái)篩選出大多數(shù)行,那么使用 INNER JOIN 會(huì)獲得同樣的結(jié)果,而且效率高得多。如果需要為所有的可能性都返回?cái)?shù)據(jù)(例如在您希望使用每月銷(xiāo)售日期填充一個(gè)圖表時(shí)),則笛卡爾乘積可能會(huì)非常有幫助。但是,您不應(yīng)該將它們用于其他用途,因?yàn)樵诖蠖鄶?shù)方案中 INNER JOIN 的效率要高得多。
?? 拾遺補(bǔ)零?
??? 這里介紹其他一些可幫助提高 SQL 查詢(xún)效率的常用技術(shù)。假設(shè)您將按區(qū)域?qū)λ袖N(xiāo)售人員進(jìn)行分組并將他們的銷(xiāo)售額進(jìn)行小計(jì),但是您只想要那些數(shù)據(jù)庫(kù)中標(biāo)記為處于活動(dòng)狀態(tài)的銷(xiāo)售人員。您可以按區(qū)域?qū)︿N(xiāo)售人員分組,并使用 HAVING 子句消除那些未處于活動(dòng)狀態(tài)的銷(xiāo)售人員,也可以在 WHERE 子句中執(zhí)行此操作。在 WHERE 子句中執(zhí)行此操作會(huì)減少需要分組的行數(shù),所以比在 HAVING 子句中執(zhí)行此操作效率更高。HAVING 子句中基于行的條件的篩選會(huì)強(qiáng)制查詢(xún)對(duì)那些在 WHERE 子句中會(huì)被去除的數(shù)據(jù)進(jìn)行分組。
??? 另一個(gè)提高效率的技巧是使用 DISTINCT 關(guān)鍵字查找數(shù)據(jù)行的單獨(dú)報(bào)表,來(lái)代替使用 GROUP BY 子句。在這種情況下,使用 DISTINCT 關(guān)鍵字的 SQL 效率更高。請(qǐng)?jiān)谛枰?jì)算聚合函數(shù)(SUM、COUNT、MAX 等)的情況下再使用 GROUP BY。另外,如果您的查詢(xún)總是自己返回一個(gè)唯一的行,則不要使用 DISTINCT 關(guān)鍵字。在這種情況下,DISTINCT 關(guān)鍵字只會(huì)增加系統(tǒng)開(kāi)銷(xiāo)。
??? 您已經(jīng)看到了,有大量技術(shù)都可用于優(yōu)化查詢(xún)和實(shí)現(xiàn)特定的業(yè)務(wù)規(guī)則,技巧就是進(jìn)行一些嘗試,然后比較它們的性能。最重要的是要測(cè)試、測(cè)試、再測(cè)試。在此專(zhuān)欄的將來(lái)各期內(nèi)容中,我將繼續(xù)深入講述 SQL Server 概念,包括數(shù)據(jù)庫(kù)設(shè)計(jì)、好的索引實(shí)踐以及 SQL Server 安全范例。
關(guān)鍵詞標(biāo)簽:方法,性能,提高,使用,
相關(guān)閱讀
熱門(mén)文章 淺談JSP JDBC來(lái)連接SQL Server 2005的方法 SqlServer2005對(duì)現(xiàn)有數(shù)據(jù)進(jìn)行分區(qū)具體步驟 sql server系統(tǒng)表?yè)p壞的解決方法 MS-SQL2005服務(wù)器登錄名、角色、數(shù)據(jù)庫(kù)用戶(hù)、角色、架構(gòu)的關(guān)系
人氣排行 配置和注冊(cè)O(shè)DBC數(shù)據(jù)源-odbc數(shù)據(jù)源配置教程 如何遠(yuǎn)程備份(還原)SQL2000數(shù)據(jù)庫(kù) SQL2000數(shù)據(jù)庫(kù)遠(yuǎn)程導(dǎo)入(導(dǎo)出)數(shù)據(jù) SQL2000和SQL2005數(shù)據(jù)庫(kù)服務(wù)端口查看或修改 修改Sql Server唯一約束教程 SQL Server 2005降級(jí)到2000的正確操作步驟 sql server系統(tǒng)表?yè)p壞的解決方法 淺談JSP JDBC來(lái)連接SQL Server 2005的方法