最近项目上有一个功能,要求是实时采集和更新可售商品记录。可售商品信息来源于另外一套销售系统的接口。
通常可售商品记录在8万条左右,采集的数据不提供主键,按照一系列的字段内容识别是否相同,好多字段为null值。(所以无法进行主键比较)
采集和保存的业务规则是:
从采集的数据中对照数据库中,采集的数据中存在而库中不存在的商品,属于新增加的商品记录,需要增加到库中。
而库中存在但采集的数据中不存在的,则属于已经销售的商品,需要从库中删除。
为此查阅了很多xdjm的代码,给出了若干解决方法。
可惜在数据量很小的情况还行,当使用实际数据进行时,对照时间已经不能忍受。
于是只能苦思冥想,自行写出了一个方法,贴出来供大家研究,如果发现有bug,请及时告知。
2012-5-10 PS
很感谢一直以来关注本帖的朋友们,原来的代码不是很完善,后来修改了也一直不曾更新,换了个思路重构了代码,如果发现有误请留言。
新代码解决了现有的几个bug,也接受了一些朋友的建议,未经测试,请检查后使用。
View Code
1 /// <summary>
2 /// 比较两个DataTable数据(结构相同,字段名不同)
3 /// </summary>
4 /// <param name="dtDest">来自数据库的DataTable</param>
5 /// <param name="dtSrc">来自文件的DataTable</param>
6 /// <param name="dtRetAdd">新增数据(dt2中的数据)</param>
7 /// <param name="dtRetDel">删除的数据(dt2中的数据)</param>
8 /// <param name="srcKeyFields">源关键字段名</param>
9 /// <param name="destKeyFields">目标关键字段名,与源关键字段名对应</param>
10 public static void CompareDt(DataTable dtSrc, DataTable dtDest, out DataTable dtRetAdd, out DataTable dtRetDel, string srcKeyFields, string destKeyFields)
11 {
12 //源记录集与目标记录集有一个为null则退出
13 if (dtSrc == null || dtDest == null)
14 {
15 dtRetDel = null;
16 dtRetAdd = null;
17 return;
18 }
19 //定义返回记录表
20 dtRetDel = dtSrc.Clone();
21 dtRetAdd = dtRetDel.Clone();
22 //参照列为空则退出
23 if (string.IsNullOrEmpty(srcKeyFields) || string.IsNullOrEmpty(destKeyFields))
24 return;
25 //获得参照列列表
26 string[] srcFields = srcKeyFields.Split(',');//列名数组
27 string[] destFields = destKeyFields.Split(',');//列名数组
28 //参照列数目不一致则退出
29 if (srcFields.Length != destFields.Length)
30 return;
31 //按参照列排序源表和目标表
32 DataRow[] drSrc = dtSrc.Select("", srcKeyFields);
33 DataRow[] drDest = dtDest.Select("", destKeyFields);
34 //定义源表和目标表长度
35 int iSrcCount = drSrc.Length;
36 int iDestCount = drDest.Length;
37 //源表为空则目标表全部加入删除队列并返回
38 if (iSrcCount == 0)
39 {
40 foreach (DataRow row in drDest)
41 {
42 dtRetDel.Rows.Add(row.ItemArray);
43 }
44 return;
45 }
46 //目标表为空则源表全部加入新增队列并返回
47 if (iDestCount == 0)
48 {
49 foreach (DataRow row in drSrc)
50 {
51 dtRetAdd.Rows.Add(row.ItemArray);
52 }
53 return;
54 }
55 //定义源表和目标表指针
56 int iSrc = 0;
57 int iDest = 0;
58 //开始循环比对
59 while (iSrc < iSrcCount && iDest < iDestCount)
60 {
61 //定义列比对结果
62 int result = 0;
63 object oSrc;
64 object oDest;
65 //循环比对列值
66 for (int colIndex = 0; colIndex < srcFields.Length; colIndex++)
67 {
68 //获得列值
69 oSrc = drSrc[iSrc][srcFields[colIndex]];
70 oDest = drDest[iDest][destFields[colIndex]];
71 //比较列值,不相等则退出循环
72 if (oSrc == DBNull.Value)
73 {
74 result = oDest == DBNull.Value ? 0 : -1;
75 }
76 else
77 {
78 result = oDest == DBNull.Value ? 1 : string.Compare(oSrc.ToString(), oDest.ToString(), false);
79 }
80 if (result != 0)
81 break;
82 }
83 //检查行比较结果
84 switch (result)
85 {
86 ////源表小则将源表本行加入新增队列,同时移动源表指针
87 case -1:
88 dtRetAdd.Rows.Add(drSrc[iSrc].ItemArray);
89 iSrc++;
90 break;
91 ////相同两表同时移动指针
92 case 0:
93 iSrc++;
94 iDest++;
95 break;
96 ////目标表小则将目标表本行加入删除队列,同时移动目标表指针
97 case 1:
98 dtRetDel.Rows.Add(drDest[iDest].ItemArray);
99 iDest++;
100 break;
101 default:
102 break;
103 }
104 }
105 //源表到达最后一条,目标表未到达,则目标表剩余行全部加入删除队列
106 if (iDest < iDestCount)
107 {
108 for (int index = iDest; index < iDestCount; index++)
109 {
110 dtRetDel.Rows.Add(drDest[index].ItemArray);
111 }
112 }
113 //目标表到达最后一条,源表未到达,则源表剩余行全部加入新增队列
114 else if (iSrc < iSrcCount)
115 {
116 for (int index = iSrc; index < iSrcCount; index++)
117 {
118 dtRetAdd.Rows.Add(drSrc[index].ItemArray);
119 }
120 }
121
2 /// 比较两个DataTable数据(结构相同,字段名不同)
3 /// </summary>
4 /// <param name="dtDest">来自数据库的DataTable</param>
5 /// <param name="dtSrc">来自文件的DataTable</param>
6 /// <param name="dtRetAdd">新增数据(dt2中的数据)</param>
7 /// <param name="dtRetDel">删除的数据(dt2中的数据)</param>
8 /// <param name="srcKeyFields">源关键字段名</param>
9 /// <param name="destKeyFields">目标关键字段名,与源关键字段名对应</param>
10 public static void CompareDt(DataTable dtSrc, DataTable dtDest, out DataTable dtRetAdd, out DataTable dtRetDel, string srcKeyFields, string destKeyFields)
11 {
12 //源记录集与目标记录集有一个为null则退出
13 if (dtSrc == null || dtDest == null)
14 {
15 dtRetDel = null;
16 dtRetAdd = null;
17 return;
18 }
19 //定义返回记录表
20 dtRetDel = dtSrc.Clone();
21 dtRetAdd = dtRetDel.Clone();
22 //参照列为空则退出
23 if (string.IsNullOrEmpty(srcKeyFields) || string.IsNullOrEmpty(destKeyFields))
24 return;
25 //获得参照列列表
26 string[] srcFields = srcKeyFields.Split(',');//列名数组
27 string[] destFields = destKeyFields.Split(',');//列名数组
28 //参照列数目不一致则退出
29 if (srcFields.Length != destFields.Length)
30 return;
31 //按参照列排序源表和目标表
32 DataRow[] drSrc = dtSrc.Select("", srcKeyFields);
33 DataRow[] drDest = dtDest.Select("", destKeyFields);
34 //定义源表和目标表长度
35 int iSrcCount = drSrc.Length;
36 int iDestCount = drDest.Length;
37 //源表为空则目标表全部加入删除队列并返回
38 if (iSrcCount == 0)
39 {
40 foreach (DataRow row in drDest)
41 {
42 dtRetDel.Rows.Add(row.ItemArray);
43 }
44 return;
45 }
46 //目标表为空则源表全部加入新增队列并返回
47 if (iDestCount == 0)
48 {
49 foreach (DataRow row in drSrc)
50 {
51 dtRetAdd.Rows.Add(row.ItemArray);
52 }
53 return;
54 }
55 //定义源表和目标表指针
56 int iSrc = 0;
57 int iDest = 0;
58 //开始循环比对
59 while (iSrc < iSrcCount && iDest < iDestCount)
60 {
61 //定义列比对结果
62 int result = 0;
63 object oSrc;
64 object oDest;
65 //循环比对列值
66 for (int colIndex = 0; colIndex < srcFields.Length; colIndex++)
67 {
68 //获得列值
69 oSrc = drSrc[iSrc][srcFields[colIndex]];
70 oDest = drDest[iDest][destFields[colIndex]];
71 //比较列值,不相等则退出循环
72 if (oSrc == DBNull.Value)
73 {
74 result = oDest == DBNull.Value ? 0 : -1;
75 }
76 else
77 {
78 result = oDest == DBNull.Value ? 1 : string.Compare(oSrc.ToString(), oDest.ToString(), false);
79 }
80 if (result != 0)
81 break;
82 }
83 //检查行比较结果
84 switch (result)
85 {
86 ////源表小则将源表本行加入新增队列,同时移动源表指针
87 case -1:
88 dtRetAdd.Rows.Add(drSrc[iSrc].ItemArray);
89 iSrc++;
90 break;
91 ////相同两表同时移动指针
92 case 0:
93 iSrc++;
94 iDest++;
95 break;
96 ////目标表小则将目标表本行加入删除队列,同时移动目标表指针
97 case 1:
98 dtRetDel.Rows.Add(drDest[iDest].ItemArray);
99 iDest++;
100 break;
101 default:
102 break;
103 }
104 }
105 //源表到达最后一条,目标表未到达,则目标表剩余行全部加入删除队列
106 if (iDest < iDestCount)
107 {
108 for (int index = iDest; index < iDestCount; index++)
109 {
110 dtRetDel.Rows.Add(drDest[index].ItemArray);
111 }
112 }
113 //目标表到达最后一条,源表未到达,则源表剩余行全部加入新增队列
114 else if (iSrc < iSrcCount)
115 {
116 for (int index = iSrc; index < iSrcCount; index++)
117 {
118 dtRetAdd.Rows.Add(drSrc[index].ItemArray);
119 }
120 }
121