同事說,使用存儲過程挺香的!阿里巴巴爲什麼要禁止?

點擊上方“芋道源碼”,選擇“設爲星標

管她前浪,還是後浪?

能浪的浪,纔是好浪!

每天 8:55 更新文章,每天掉億點點頭髮...

源碼精品專欄

 

來源:sf.ggs/a/1190000011138993

之所以有這個題目,我既不是故意吸引眼球,也不想在本文對存儲過程進行教科書般論述。最近項目中遇到的存儲過程問題,讓我想起了去年在武漢出差時一位同事的發問:

我覺得存儲過程挺好用的,爲什麼你不建議用?

當時我好似胸有萬言,但終究沒用一個實在的例子回答同事,只是從結論上大侃一通,代碼相對於SQL,複用、擴展、通用性都要更強。想必同事並不信服。

現在想來,我最近正碰到的問題,算是一個可以回答同事的例子吧。

最近項目中有個新需求,需要校驗一個用戶是否有Job,Certification,Disclosure這三個業務數據。

翻看了代碼發現,系統的用戶個人頁面的C#代碼調用了三個存儲過程,去抓取用戶的Job,Certification,Disclosure數據。我的新需求,自然需要複用這三個存儲過程,否則:

若每一處都寫一次抓取數據的業務邏輯代碼,若業務邏輯發生變化,難以追查和維護所有讀取Job,Certification,Disclosure的SQL。

如果我在C#代碼中調用這已有的三個存儲過程,事情本該非常快就能結束。我也是這麼做的。

但code reviewer認爲,我的需求中,並不需要Job,Certification,Disclosure這三個業務對象的數據。我只是需要給定用戶是否有Job,Certification,Disclosure而已。所以我應將是否有無Job,Certification,Disclosure的判斷邏輯寫在數據庫,最終通過網絡從數據庫傳到web服務器的僅是true或false,節省網絡流量,這樣最好不過了。也對。除開網絡性能,從接口設計的角度講,接口的傳入和返回值,都應是你本身需要的數據,不應帶有大量不需要或者需要caller去預處理的數據。從接口語義表達就可知調用的目的,這樣代碼可讀性也會有大大提高。

那就動手改。但沒想到的是問題來了。

爲了講述問題,我簡化代碼,假設系統現有的存儲過程如下:

CREATE PROCEDURE [dbo].[GetJobs]
(
    @PersonId int,
    @OrganizaitionId int
 )
AS
BEGIN
  SELECT JobId,JobName,JobType FROM Job WHERE PersonId = @PersonId AND OrganizaitionId = @OrganizaitionId
END

我在新的存儲過程中調用它,我需要獲得該person的jobs的數量,即GetJobs返回結果集的count。

爲了實現這一目的,首先想到的是使用臨時表,將返回結果集存入臨時表,再對其進行count(*)的計數操作:

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

這種辦法簡單有效,但它存在嚴重的維護問題。未來如果被調用的存儲過程的返回結果集字段有變動,那麼MyProc中的臨時表結構也需要隨之變化。這是令人難以接受的。

那麼將MyProc中的INSERT INTO換爲SELECT INTO呢?很遺憾,答案是不行。SQL本身並不支持這種用法。

給現有存儲過程GetJobsoutput參數?本例中因爲GetJobs已被其他多處代碼或SQL scripts調用,所以對現有現有存儲過程進行改動會有不小風險。

我搜遍網絡,一位MS MVP的大神的文章幾乎總結了所有存儲過程之間傳遞數據的方法: 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的語句寫一了次。

存儲過程在很多場景時有其優勢,比如性能。但對於業務邏輯的通用方法,非常不推薦將其寫在存儲過程中,代碼複用、擴展與客戶端語言比,相差甚遠。也許終究能實現,但代價與風險比客戶端語言要高,得不償失。

天知道還有沒有機會和那位前同事再討論這一話題呢。



歡迎加入我的知識星球,一起探討架構,交流源碼。加入方式,長按下方二維碼噢

已在知識星球更新源碼解析如下:

最近更新《芋道 SpringBoot 2.X 入門》系列,已經 20 餘篇,覆蓋了 MyBatis、Redis、MongoDB、ES、分庫分表、讀寫分離、SpringMVC、Webflux、權限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能測試等等內容。

提供近 3W 行代碼的 SpringBoot 示例,以及超 4W 行代碼的電商微服務項目。

獲取方式:點“在看”,關注公衆號並回復 666 領取,更多內容陸續奉上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章