需求
一切都是空的,除了Money,只有需求才是最真的,你懂的。
最近接到个略显棘手的需求,思索再三,想出两种方法,可觉得都不太好,这里与大家讨论一下。
需求如下:
用户需要在现有的某个grid中添加新的一列: Des。现有grid的所有数据DataTable:OraDt都来自一个oracle数据库,而新加的Des值却来自别的四个sqlserver数据库中的某一个。四个SqlServer数据库CHNDB,JPNDB,USDB,CANDB的结构相同,只是数据不同。
根据两个变量:geo和desID可以从前面sql DB中中得到Des的值。geo确定从哪个sql数据库中获取,desID用来得到des的值。 geo和desID就在OraDt中存着,如何才能高效的获取des的值?
geo与sqlserver db对应关系如下:
Geo值 | Sqlserver DB |
CHN | CHNDB |
JPN | JPNDB |
US | USDB |
CAN | CANDB |
解决思路
- 从Oracle数据库的某表中查询数据,得到DataTab:OraDt;
- OraDt中添加新列: DataColumn("Des", typeof(string));
- 遍历OraDt的每一个datarow,取得不同的geo和desID的值;
- 根据geo和desID的值确定Sqlserver DB并获得des的值;
- 更新OraDt中新列row["Des]的值
解决方案伪代码
- 这种方法思路比较简单,老老实实遍历OraDt的每一个datarow,取得不同的geo和desID的值,然后从对应的sqlser数据库中获取des的值:
//得到oracle datatable,添加row[des],遍历datatable,获取des的值,更新des protected void ProcessDataTable() { //添加新列 _ds.Tables[0].Columns.Add(new DataColumn("Des", typeof(string))); string desID = string.Empty; string geo = string.Empty; //遍历OraDt foreach (DataRow row in _ds.Tables[0].Rows) { if (row["desID"] != DBNull.Value) desID = row["desID"].ToString(); if (!string.IsNullOrEmpty(desID) ) { //得到geo switch (row["countryId"].ToString()) { case Country.US: geo = "US"; break; case Country.China: geo = "CHA"; break; case Country.Canada: geo = " CAN"; break; case Country.Jpan: geo = "JPN"; break; } //得到添加des row["Des"]= GetExemptionDes(geo, desID); } else row["Des"] = string.Empty; } }
GetExemptionDes的代码:
private string GetExemptionDes(string geo, string desid) { string description = string.Empty; string query = "select des from taxDes where desid="+desid; //确定哪个DB获取数据 Database DbGEO = DbGEOFactory.CreateDatabase(geo); System.Data.Common.DbCommand sqlCmd = DbGEO.GetSqlStringCommand(query); //获得des值 using (IDataReader dr = DbGEO.ExecuteReader(sqlCmd)) { while (dr.Read()) { if (dr["des"] != null) description = dr["des"].ToString(); } dr.Close(); } return description; }
性能探究:
这种想法思路简单,问题也显而易见,虽然通常情况下OraDt只有十几个row,可极个别时候OraDt的row能达到上千个,这时候,上面的方法要连续访问 Sqlserver DB上千次,显然严重影响了性能。有某有其他的好方法呢?
第二方案,或者其他更好的解决方法?
由于geo最多只有四个,所以我们可以把相同geo下的desID拼接起来,最后一块访问sqlserver db,这样最多只需要访问四次,思路:
前三步与上面方法一样,
4.根据geo把desid拼接于一个string变量中
5.最后一起访问sqlserver db
6.获得des值,
7.OraDt更新des
第二方法缺憾:
大量datatable的操作实际上也影响了代码的效率,而且OraDt的数据量通常只有几十行,用这种方法处理普通的OraDt时候显然太过繁琐。
可是现在看来似乎只有这两种方法来解决这个问题了,难道真的就由这么个性能问题存在于我们的代码中么?哪位如果有什么新的解决思路,可以一起分享一下,一起进步。
第一种方法和第二种到底用哪一种好呢?
现将第二方案的详细代码列示如下,由于写的匆忙,还未来的及调试,有什么问题或者哪里可以改进,希望大家多多指正。
可以看到这种方法繁琐操纵OraDt,尤其是在方法UpdateOraDT中,由于某种原因不能使用Linq 来连接datatable,三个foreach 嵌套起来简直不堪入目。处理普通的OraDt时候明显会带来性能问题:
protected void ProcessDataTable() { _ds.Tables[0].Columns.Add(new DataColumn("ExemptionDes", typeof(string))); string desID = string.Empty; string geo = string.Empty; //建造一个锯齿数组,[0]:geo,[1]: 该geo是否需要被访问,默认0表示不需要,[3]用来拼接desID string[][] geoExistArray = new string[4][] { new string[] { "NA", "0","" }, new string[] { "LA", "0","" }, new string[] { "EMEA", "0","" }, new string[] { "AP", "0","" }, }; //遍历datatable foreach (DataRow row in _ds.Tables[0].Rows) { if (row["desID"] != DBNull.Value) { xmtn_ID = row["desID"].ToString(); } //如果desID为空,直接将des设置为0 //不为空,则遍历datatable,取des值 if (!string.IsNullOrEmpty(desID)) { switch (row["countryId"].ToString()) { case Country.US: geo = "US"; geoExistArray[0][0] = "1"; geoExistArray[0][2] = geoExistArray[0][2] + desID+ ","; break; case Country.China: geo = "CHN"; geoExistArray[1][0] = "1"; geoExistArray[1][2] = geoExistArray[1][2] + desID+ ","; break; case Country.Canada: geo = "CAN"; geoExistArray[2][0] = "1"; geoExistArray[2][2] = geoExistArray[2][2] + desID+ ","; break; case Country.Jpan: geo = "JPN"; geoExistArray[3][0] = "1"; geoExistArray[3][2] = geoExistArray[3][2] + desID+ ","; break; } } else row["Des"] = string.Empty; } //移除每个拼出的desID字符串中的最后一个逗号 foreach (var array in geoExistArray) { if (array[3].Length > 0) array[3].Remove(array[3].Length - 1, 1); } //获取des值并更新OraDT UpdateOraDT(_ds.Tables[0], geoExistArray); }
UpdateOraDT代码:
private DataTable UpdateOraDT(DataTable dt, string[][] geoExistArray) { DataTable ExemDesDT; foreach (var GeoArrary in geoExistArray) { //得出一个需要被访问的sql db下的所有des,存于ExemDesDT if (GeoArrary[1] == "1") { ExemDesDT = GetExemptionDesSQL(GeoArrary[0], GeoArrary[2]); if (ExemDesDT != null) //遍历ExemDesDT,如果ExemDesDT中的id_exemption等于OraDt中的desID ,则获取des foreach (DataRow pyrow in ExemDesDT.Rows) { foreach (DataRow phxrow in dt.Rows) { if (pyrow["id_exemption"] == phxrow["desID"]) phxrow["ExemptionDes"] = pyrow["description"].ToString()); } } } } return dt; }
GetExemptionDesSQL方法:
private DataTable GetExemptionDesSQL(string geo, string desidList) { DataTable ExemDesDT = new DataTable(); string query = " select des from taxDes where desid in (" + desidList+")"; Database DbGEO = DbGEOFactory.CreateDatabase(geo); System.Data.Common.DbCommand sqlCmd = DbPyramid.GetSqlStringCommand(query); ExemDesDT = DbGEO.ExecuteDataSet(sqlCmd).Tables[0]; return ExemDesDT; }