一文詳解SQL中為什么不要使用1=1
前言
最近看幾個(gè)老項(xiàng)目的SQL條件中使用了1=1,想想自己也曾經(jīng)這樣寫(xiě)過(guò),略有感觸,特別拿出來(lái)說(shuō)道說(shuō)道。
編寫(xiě)SQL語(yǔ)句就像炒菜,每一種調(diào)料的使用都可能會(huì)影響菜品的最終味道,每一個(gè)SQL條件的加入也可能會(huì)影響查詢的執(zhí)行效率。那么 1=1 存在什么樣的問(wèn)題呢?為什么又會(huì)使用呢?
為什么會(huì)使用 1=1?
在動(dòng)態(tài)構(gòu)建SQL查詢時(shí),查詢條件往往都是動(dòng)態(tài)的,最終執(zhí)行時(shí)可能會(huì)使用不同的條件。這時(shí)候,他們就會(huì)使用“1=1”作為一個(gè)始終為真的條件,讓接下來(lái)的所有條件都可以方便地用“AND”連接起來(lái),就像是搭積木的時(shí)候先放一個(gè)基座,其他的積木塊就都可以在這個(gè)基座上疊加。
就像下邊這樣:
SELECT * FROM table WHERE 1=1
<if test="username != null">
AND username = #{username}
</if>
<if test="age > 0">
AND age = #{age}
</if>
這樣就不用在增加每個(gè)條件之前先判斷是否需要添加“AND”。
1=1 帶來(lái)的問(wèn)題
性能問(wèn)題?
我們先來(lái)了解一下數(shù)據(jù)庫(kù)查詢優(yōu)化器的工作原理。查詢優(yōu)化器就像是一個(gè)聰明的圖書(shū)管理員,它知道如何最快地找到你需要的書(shū)籍。當(dāng)你告訴它所需書(shū)籍的特征時(shí),它會(huì)根據(jù)這些信息選擇最快的檢索路徑。比如你要查詢作者是“譚浩強(qiáng)”的書(shū)籍,它就選擇先通過(guò)作者索引找到書(shū)籍索引,再通過(guò)書(shū)籍索引找到對(duì)應(yīng)的書(shū)籍,而不是費(fèi)力的把所有的書(shū)籍遍歷一遍。
但是,如果我們告訴它一些無(wú)關(guān)緊要的信息,比如“我要一本書(shū),它是一本書(shū)”,這并不會(huì)幫助管理員更快地找到書(shū),反而可能會(huì)讓他覺(jué)得困惑。一個(gè)帶有“1=1”的查詢可能會(huì)讓數(shù)據(jù)庫(kù)去檢查每一條記錄是否滿足這個(gè)始終為真的條件,這就像是圖書(shū)管理員不得不檢查每一本書(shū)來(lái)確認(rèn)它們都是書(shū)一樣,顯然是一種浪費(fèi)。
你可能會(huì)說(shuō):數(shù)據(jù)庫(kù)沒(méi)有這么傻吧?
確實(shí),這實(shí)際上可能不會(huì)產(chǎn)生問(wèn)題,因?yàn)楝F(xiàn)代數(shù)據(jù)庫(kù)的查詢優(yōu)化器已經(jīng)非常智能,它們通常能夠識(shí)別出像 1=1 這樣的恒真條件,并在執(zhí)行查詢計(jì)劃時(shí)優(yōu)化掉它們。在許多情況下,即使查詢中包含了1=1,數(shù)據(jù)庫(kù)的性能也不會(huì)受到太大影響,優(yōu)化器會(huì)在實(shí)際執(zhí)行查詢時(shí)將其忽略。
但是優(yōu)化器并不是萬(wàn)能的。在某些復(fù)雜的查詢場(chǎng)景中,即使是簡(jiǎn)單的 1=1 也可能對(duì)優(yōu)化器的決策造成不必要的影響,比如導(dǎo)致全表掃描。
代碼質(zhì)量
另外從代碼質(zhì)量的角度,我們也需要避免在查詢中包含 1=1,有以下幾點(diǎn)考慮:
- 代碼清晰性:即使數(shù)據(jù)庫(kù)可以優(yōu)化掉這樣的條件,但對(duì)于閱讀SQL代碼的人來(lái)說(shuō),1=1可能會(huì)造成困惑。代碼的可讀性和清晰性非常重要,特別是在團(tuán)隊(duì)協(xié)作的環(huán)境中。
- 習(xí)慣養(yǎng)成:即使在當(dāng)前的數(shù)據(jù)庫(kù)系統(tǒng)中1=1不會(huì)帶來(lái)性能問(wèn)題,習(xí)慣了寫(xiě)不必要的代碼可能會(huì)在其他情況下引入實(shí)際的性能問(wèn)題。比如,更復(fù)雜的無(wú)用條件可能不會(huì)那么容易被優(yōu)化掉。
- 跨數(shù)據(jù)庫(kù)兼容性:不同的數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)可能有不同的優(yōu)化器能力。一個(gè)系統(tǒng)可能輕松優(yōu)化掉1=1,而另一個(gè)系統(tǒng)則可能不那么高效。編寫(xiě)不依賴于特定優(yōu)化器行為的SQL語(yǔ)句是一個(gè)好習(xí)慣。
編寫(xiě)盡可能高效、清晰和準(zhǔn)確的SQL語(yǔ)句,不僅有助于保持代碼的質(zhì)量,也讓代碼具有更好的可維護(hù)性和可擴(kuò)展性。
替代 1=1 的更佳做法
現(xiàn)在開(kāi)發(fā)者普遍使用ORM框架來(lái)操作數(shù)據(jù)庫(kù)了,還在完全手寫(xiě)拼SQL的同學(xué)可能需要反思下了,這里給兩個(gè)不同ORM框架下替代1=1的方法。
假設(shè)我們有一個(gè)用戶信息表 user,并希望根據(jù)傳入的參數(shù)動(dòng)態(tài)地過(guò)濾用戶。
首先是Mybatis:
<!-- MyBatis映射文件片段 -->
<select id="selectUsersByConditions" parameterType="map" resultType="com.example.User">
SELECT * FROM user
<where>
<!-- 使用if標(biāo)簽動(dòng)態(tài)添加條件 -->
<if test="username != null and username != ''">
AND username = #{username}
</if>
<if test="age > 0">
AND age = #{age}
</if>
<!-- 更多條件... -->
</where>
</select>
在 MyBatis 中,避免使用 1=1 的典型方法是利用動(dòng)態(tài)SQL標(biāo)簽(如 <if>)來(lái)構(gòu)建條件查詢。<where> 標(biāo)簽會(huì)自動(dòng)處理首條條件前的 AND 或 OR。當(dāng)沒(méi)有滿足條件的 <if> 或其他條件標(biāo)簽時(shí),<where> 標(biāo)簽內(nèi)部的所有內(nèi)容都會(huì)被忽略,從而不會(huì)生成多余的 AND 或 WHERE 子句。
再看看 Entity Framework 的方法:
var query = context.User.AsQueryable();
if (!string.IsNullOrEmpty(username))
{
query = query.Where(b => b.UserName.Contains(username));
}
if (age>0)
{
query = query.Where(b => b.Age = age);
}
var users = query.ToList();
這是一種函數(shù)式編程的寫(xiě)法,最終生成SQL時(shí),框架會(huì)決定是否在條件前增加AND,而不需要人為的增加 1=1。
總結(jié)
“1=1”在SQL語(yǔ)句中可能看起來(lái)無(wú)害,但實(shí)際上它是一種不良的編程習(xí)慣,可能會(huì)導(dǎo)致性能下降。就像在做飯時(shí)不會(huì)無(wú)緣無(wú)故地多加調(diào)料一樣,我們?cè)诰帉?xiě)SQL語(yǔ)句時(shí)也應(yīng)該避免添加無(wú)意義的條件。
每一行代碼都應(yīng)該有它存在的理由,不要讓人和數(shù)據(jù)庫(kù)浪費(fèi)時(shí)間在不必要的事情上。
到此這篇關(guān)于SQL中為什么不要使用1=1的文章就介紹到這了,更多相關(guān)SQL為何不要使用1=1內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SQL中游標(biāo)(cursor)的基本使用實(shí)例
當(dāng)你檢索的數(shù)據(jù)只是一條記錄時(shí),你所編寫(xiě)的事務(wù)語(yǔ)句代碼往往使用SELECT INSERT語(yǔ)句,但如果從某一結(jié)果集中逐一地讀取一條記錄呢?游標(biāo)為我們提供了一種極為優(yōu)秀的解決方案,這篇文章主要給大家介紹了關(guān)于SQL中游標(biāo)(cursor)基本使用的相關(guān)資料,需要的朋友可以參考下2021-11-11
關(guān)于面試中常問(wèn)的數(shù)據(jù)庫(kù)回表問(wèn)題
這篇文章主要介紹了關(guān)于面試中常問(wèn)的數(shù)據(jù)庫(kù)回表問(wèn)題,回表就是先通過(guò)數(shù)據(jù)庫(kù)索引掃描出數(shù)據(jù)所在的行,再通過(guò)行主鍵id取出索引中未提供的數(shù)據(jù),即基于非主鍵索引的查詢需要多掃描一棵索引樹(shù),需要的朋友可以參考下2023-07-07
在Windows下自動(dòng)備份PostgreSQL的教程
這篇文章主要介紹了在Windows下自動(dòng)備份PostgreSQL的教程,主要通過(guò)編寫(xiě)一個(gè)簡(jiǎn)單的批處理腳本,需要的朋友可以參考下2015-04-04
錯(cuò)誤代碼:1100 Table ''t_depart_info'' was not locked with LOCK T
這篇文章就是告訴大家如何解決錯(cuò)誤代碼:1100 Table 't_depart_info' was not locked with LOCK TABLES,遇到類似問(wèn)題的朋友可以參考一下2015-10-10
復(fù)雜SQL實(shí)現(xiàn)分組分情況分頁(yè)查詢代碼實(shí)例
最近學(xué)習(xí)了一下SQL的分頁(yè)查詢,總結(jié)了復(fù)雜SQL分組分頁(yè)查詢的方法,這篇文章主要給大家介紹了關(guān)于復(fù)雜SQL實(shí)現(xiàn)分組分情況分頁(yè)查詢的相關(guān)資料,需要的朋友可以參考下2023-12-12
存儲(chǔ)過(guò)程返回?cái)?shù)組對(duì)象示例代碼
存儲(chǔ)過(guò)程返回?cái)?shù)組對(duì)象其實(shí)就相當(dāng)于返回List里面放的對(duì)象數(shù)據(jù),下面與大家分享是例子,感興趣的朋友可以學(xué)習(xí)下2013-07-07
SQL注入技巧之顯注與盲注中過(guò)濾逗號(hào)繞過(guò)詳析
SQL注入的繞過(guò)技巧有很多,下面這篇文章主要給大家介紹了關(guān)于SQL注入技巧之顯注與盲注中過(guò)濾逗號(hào)繞過(guò)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
mycat在windows環(huán)境下的安裝和啟動(dòng)
這篇文章主要介紹了mycat在windows環(huán)境下的安裝和啟動(dòng)過(guò)程,需要的朋友參考下吧2018-03-03

