一線程序員和 sa 總是相恨相殺,這話確實不假,吐槽這里就不多講,項目快開發完的時候,讓之前各個模塊的增刪改操作全部都先放入對應的臨時表(增加一狀態欄位Status,來表示增、刪、改)中,然后在主管放行界面放行之后,數據才算真正入庫。sa 輕輕一句話,整個項目幾乎從頭到尾要改一遍。雖然已不是第一次做此事。但着實還是費了一番氣力。期間遇到了不少問題很值得記錄。
流程大致如上,第一排是原邏輯,第二排是增加主管放行的新邏輯(隨手畫的,比較簡陋)
原項目中涉及到,多表主外鍵關系,新增范本功能等較為復雜的邏輯。這里在后面會做一個大致的流程說明
由於各表的欄位,主外鍵關系不同,所以第一次開發時,均是對維護的每個表(功能)建立對應的Biz(業務處理類)來實現增刪改查。
新需求要求用戶對數據的刪、改、查操作均 添加到對應的temp表中,如 表A 欄位 B,C,D -對應- 臨時表TempA 欄位 B,C,D,Status
1 .操作某表數據
在對表進行 新增、修改、刪除 操作之前,都要先到臨時表中去撈取,看是否該數據已存在待主管放行的臨時表中,這里可直接寫一個公共方法,傳表名 和 聯合主鍵即可
(PS:新增、修改傳入的是一筆聯合主鍵值,故需要包裝,兩側加單引號,刪除的話,前台ajax提交時,已經添加過單引號)
public bool IsExistTemp(string tbname, string strtablekey,string operatype) { try { //in操作 新增、修改傳入值需要包裝下 if (operatype != "D") { strtablekey = "'" + strtablekey + "'"; } string strsql = "SELECT COUNT(*) FROM " + tbname + " AS a WHERE a.TableKey IN(" + strtablekey + ") "; int row = (int)base.ExecuteScalar(strsql); return row > 0; } catch (Exception e) { return false; } }
①執行新增、修改
新增界面點擊確定、和修改界面點擊確定 均是對一筆數據數據操作,直接采用Ajax的 方式,調用serializeArray()方法提交整個表單的數據 ,到Action中
對新增來說: A 判斷新增的這筆數據是否已在表中存在 B 判斷該筆數據是否已存在待主管放行的臨時表中 C 將該數據添加至臨時表中
對修改來說: A 第一次跳轉至修改界面之前,先去該筆數據是否已存在待主管放行的臨時表中,若在,修改界面給出提示,保存按鈕為灰色不可點擊
B 將該數據添加至臨時表中
相對前期判斷沒什么好說的,至於某筆數據檢核無誤后(通過A、B)條件, 添加至臨時表中,新增、刪除的邏輯代碼是一樣的,所以在Action中直接調用同一個邏輯類中的方法即可。
②執行刪除
查詢結果界面,可勾選多筆數據,進行刪除操作,這就意味着需要往臨時表中添加多筆數據。前台Ajax提交時,先遍歷復選框,然后將復選框勾選狀態所在行的主鍵Key,進行包裝再傳遞。
前面的博客中,我做的處理是循環刪除,用List<string> 接受傳來的主鍵集合,然后遍歷該集合, 比較費事,這里我直接將主鍵進行拼接,作為一個string字符串來進行傳遞
KeyList.push(tabkey);
tablekeys = "'" + KeyList.join("','") + "'";
這樣后台Action中接收的樣式就是 ''k1','k2','k3'...'
后台接收到主鍵拼接的字符串之后,傳入 原表名稱,目的表名稱,聯合主鍵值,目的表的Status欄位值 四個參數即可,公共方法如下
/// <summary> /// 新需求-刪除(為主管放行而寫),實則將A表數據寫到B表中 /// </summary> /// <param name="tbname">原表</param> /// <param name="tbnameTo">目的表</param> /// <param name="tablekeys">聯合主鍵</param> /// <param name="status">操作狀態(D:刪除)</param> /// <returns></returns> public bool Delete_AddTemp(string tbname, string tbnameTo, string tablekeys, char status) { try { string strsql = @"INSERT INTO " + tbnameTo + " SELECT *,'" + status + "' FROM " + tbname + " WHERE TableKey IN (" + tablekeys + ")"; //執行Sql,返回影響的行數,並判斷 return base.ExecuteNonQuery(strsql) > 0; } catch (Exception ex) { return false; } }
寫到這里,關於對原表的操作,就完成了,邏輯細分下來,實際上沒有多少代碼量
2. 主管放行界面的查詢
前面用戶操作多個表之后,將數據寫入到對應的Temp表中,來等待主管審批(放行\刪除)。 這里查詢界面寫一下拉列表,選中某表,點擊查詢,后台返回對應的視圖來顯示該臨時表數據,為了更好的拓展以及后期的維護,這里對應的視圖目錄,每一個表寫一個視圖,這樣控制器在查詢Action方法中,根據前台傳的表名,返回對應表所在的View即可。查詢操作比較基礎,沒什么值得寫的。寥寥幾句,敘述清楚流程即可。
3. 主管放行
主管放行這里邏輯較為復雜,遇到了不少問題,剛開始就寫一存儲過程,命名為sp_TableName,里面嵌入一事務,將某表的多個語句(增、刪、改)寫在一起,執行成功就提交事務,失敗就回滾。這樣的思路的話,只需要一個表對應一個存儲過程,調用時傳入存儲過程名, 和聯合主鍵值。於是乎,洋洋灑灑的將存儲過程寫完。極度簡化后刪除的代碼如下
--刪除 DELETE FROM A WHERE TableKey IN (SELECT TableKey FROM tempA WHERE sFlag = 'D' AND TableKey IN(@str)) --清空臨時資源表 DELETE FROM tempZT_SysConfig_Master WHERE TableKey IN (@str);
結果調用存儲過程時候總是執行失敗,在T-sql中執行存儲過程是成功的,但是在MVC中調用存儲過程時,老是執行返回影響行數為0,后來查看日志后發現,傳入的主鍵參數在SQL中解析時有問題,這里用的是聯合鍵,格式為 A,B (A、B欄位為主鍵,TableKey欄位為聯合主鍵,將表主鍵用逗號隔開后進行拼接),MVC中調用時傳入的聯合主鍵為
string keys ="''a1,b1','a2,b2','a3,b3''";
執行到SQL中時,解析的@str 並不是 ''a1,b1','a2,b2','a3,b3'' ,在打開了不少web界面之后,找了一種解決方案 就是用 exec('') 將sql語句包裹起來,如下
EXEC(' --刪除 DELETE FROM A WHERE TableKey IN (SELECT TableKey FROM tempA WHERE sFlag = ''D'' AND TableKey IN('+@str+')) --清空臨時資源表 DELETE FROM tempZT_SysConfig_Master WHERE TableKey IN ('+@str+'); ')
總算解決了,后來在測試中又遇到了新問題,主外鍵約束,由於原表中存在主外鍵約束,而對應的臨時表Temp是沒有主外鍵關系的,如果有的話,主次顛倒不少,邏輯就更加復雜而且無效
從表新增約束
假如某用戶登錄系統后對主表A增加幾筆數據,然后在從表B中添加了幾筆對應的數據,當主管放行臨時表B的這筆數據時,由於主管還沒放行臨時表A的這筆數據,所以執行時就會異常。
這里只需要給一提示,請先放行主表中的新增數據即可,查詢是否有從表新增約束的核心sql 如下,判斷返回值是否大於0 即可。
SELECT COUNT(a.TableKey) FROM( SELECT distinct b.TableKey FROM tempMainA AS a INNER JOIN tempMinorA AS b ON a.s1 = b.s1 AND a.s2 = b.s2 ) AS a WHERE a.TableKey IN(@tablekeys)
主表刪除約束
假如某用戶登錄系統后對主表刪除幾筆數據,當主管放行臨時表此批數據時,調用存儲過程執行到對應的刪除T-SQl語句時,由於外鍵約束,從表對應的數據還在,所以就會報異常
這里主需要在存儲過程中刪除語句的sql上面,寫入刪除從表的sql即可。不過這里由於表的特殊性,在獲取從表聯合主鍵時着實下了不少功夫,刪除從表的SQL如下:
--刪除從表 DELETE FROM Minor WHERE TableKey IN( SELECT a.TableKey FROM Minor AS a WHERE substring(a.TableKey,1,len(a.TableKey) - CHARINDEX('','',reverse(a.TableKey))) IN( SELECT TableKey FROM tempMain WHERE status = 'D' AND TableKey IN('+@str+')) )
從表聯合主鍵為 'a1,a2,a3' , 主表聯合主鍵為 'a1,a2' ,所以先撈取執行刪除操作的主表的聯合主鍵 tablekey(第三行),然后通過截取從表的聯合主鍵Tablekey in (第二行)得到從表需要刪除的TableKey,最后執行刪除從表操作.
4. 主管刪除
關於主管刪除 ,其實就是清空Temp表的記錄。對於放行來說,其邏輯就相當的簡單,沒有必要去循環刪除,直接如上操作,后台接收前台傳入主鍵拼接后的string字符串和表名,然后在邏輯類中寫入delete的T-sql即可
DELETE FROM " + tbname + " WHERE TableKey IN(" + tablekeys+ ")
執行操作,完成主管刪除
總結:寫到這里,關於主管放行的簡體版本的大致流程已經敘述完了,說白了,放行實際上就是將 臨時表的數據剪切至原表,沒錯,就是剪切,實現剪切操作時,T-SQL的能力要有,自認為我的水平遠不如老大,原項目中本來我就寫有對數據的刪、改、增操作。所以我第一次的思路是主管放行時,一筆一筆的去執行,傳入實體,調用原始的刪、改、增方法。這樣效率低不說,傳入實體的邏輯寫起來也相當費勁。最后在老大手把手的教授下,通過存儲過程可放行臨時表中所有勾選的數據,着實提高了效率。
---市人皆大笑,舉手揶揄之