為什么阿里巴巴禁止使用存儲過程?
來源:sf.gg//a/1190000011138993
之所以有這個題目,我既不是故意吸引眼球,也不想在本文對存儲過程進(jìn)行教科書般論述。最近項(xiàng)目中遇到的存儲過程問題,讓我想起了去年在武漢出差時一位同事的發(fā)問:
我覺得存儲過程挺好用的,為什么你不建議用?
當(dāng)時我好似胸有萬言,但終究沒用一個實(shí)在的例子回答同事,只是從結(jié)論上大侃一通,代碼相對于SQL,復(fù)用、擴(kuò)展、通用性都要更強(qiáng)。想必同事并不信服。
現(xiàn)在想來,我最近正碰到的問題,算是一個可以回答同事的例子吧。
最近項(xiàng)目中有個新需求,需要校驗(yàn)一個用戶是否有Job,Certification,Disclosure這三個業(yè)務(wù)數(shù)據(jù)。
翻看了代碼發(fā)現(xiàn),系統(tǒng)的用戶個人頁面的C#代碼調(diào)用了三個存儲過程,去抓取用戶的Job,Certification,Disclosure數(shù)據(jù)。我的新需求,自然需要復(fù)用這三個存儲過程,否則:
若每一處都寫一次抓取數(shù)據(jù)的業(yè)務(wù)邏輯代碼,若業(yè)務(wù)邏輯發(fā)生變化,難以追查和維護(hù)所有讀取Job,Certification,Disclosure的SQL。
如果我在C#代碼中調(diào)用這已有的三個存儲過程,事情本該非常快就能結(jié)束。我也是這么做的。
但code reviewer認(rèn)為,我的需求中,并不需要Job,Certification,Disclosure這三個業(yè)務(wù)對象的數(shù)據(jù)。我只是需要給定用戶是否有Job,Certification,Disclosure而已。所以我應(yīng)將是否有無Job,Certification,Disclosure的判斷邏輯寫在數(shù)據(jù)庫,最終通過網(wǎng)絡(luò)從數(shù)據(jù)庫傳到web服務(wù)器的僅是true或false,節(jié)省網(wǎng)絡(luò)流量,這樣最好不過了。也對。除開網(wǎng)絡(luò)性能,從接口設(shè)計(jì)的角度講,接口的傳入和返回值,都應(yīng)是你本身需要的數(shù)據(jù),不應(yīng)帶有大量不需要或者需要caller去預(yù)處理的數(shù)據(jù)。從接口語義表達(dá)就可知調(diào)用的目的,這樣代碼可讀性也會有大大提高。
那就動手改。但沒想到的是問題來了。
為了講述問題,我簡化代碼,假設(shè)系統(tǒng)現(xiàn)有的存儲過程如下:
CREATE PROCEDURE [dbo].[GetJobs]
(
@PersonId int,
@OrganizaitionId int
)
AS
BEGIN
SELECT JobId,JobName,JobType FROM Job WHERE PersonId = @PersonId AND OrganizaitionId = @OrganizaitionId
END
我在新的存儲過程中調(diào)用它,我需要獲得該person的jobs的數(shù)量,即GetJobs返回結(jié)果集的count。
為了實(shí)現(xiàn)這一目的,首先想到的是使用臨時表,將返回結(jié)果集存入臨時表,再對其進(jìn)行count(*)的計(jì)數(shù)操作:
CREATE PROCEDURE [dbo].[MyProc]
(
@PersonId int,
@OrganizaitionId int,
)
AS
BEGIN
CREATE TABLE #Temp(
PersonId int,
OrganizaitionId int
)
INSERT INTO #Temp EXEC dbo.GetJobs
@PersonId = @PersonId,
@ParentOrgId = @ParentOrgId
SELECT COUNT(*) FROM #Temp
END
這種辦法簡單有效,但它存在嚴(yán)重的維護(hù)問題。未來如果被調(diào)用的存儲過程的返回結(jié)果集字段有變動,那么MyProc中的臨時表結(jié)構(gòu)也需要隨之變化。這是令人難以接受的。
那么將MyProc中的INSERT INTO換為SELECT INTO呢?很遺憾,答案是不行。SQL本身并不支持這種用法。
給現(xiàn)有存儲過程GetJobs加output參數(shù)?本例中因?yàn)镚etJobs已被其他多處代碼或SQL scripts調(diào)用,所以對現(xiàn)有現(xiàn)有存儲過程進(jìn)行改動會有不小風(fēng)險。
我搜遍網(wǎng)絡(luò),一位MS MVP的大神的文章幾乎總結(jié)了所有存儲過程之間傳遞數(shù)據(jù)的方法: How to Share Data between Stored Procedures。他在文章中也無可奈何地說道
Keep in mind that compared to languages such as C# and Java, Transact-SQL is poorly equipped for code reuse, why solutions in T?SQL to reuse code are clumsier.
最終我沒能找到一種滿意的辦法,無奈之下我在新寫的存儲過程中將查詢Jobs的語句寫一了次。
存儲過程在很多場景時有其優(yōu)勢,比如性能。但對于業(yè)務(wù)邏輯的通用方法,非常不推薦將其寫在存儲過程中,代碼復(fù)用、擴(kuò)展與客戶端語言比,相差甚遠(yuǎn)。也許終究能實(shí)現(xiàn),但代價與風(fēng)險比客戶端語言要高,得不償失。
天知道還有沒有機(jī)會和那位前同事再討論這一話題呢。
1. BeanUtils、BeanCopier、Dozer、Orika 哪個性能最強(qiáng)?
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點(diǎn)“在看”,關(guān)注公眾號并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。
謝謝支持喲 (*^__^*)

