如何基于现有DataSet执行SQL语句创建过滤后的第二个DataSet
实现方式说明
首先明确核心前提:DataSet是离线内存数据容器,本身没有内置SQL解析执行引擎,无法直接对其运行原生跨表SQL语句,所有跨表过滤逻辑都需要基于内存数据遍历完成,以下是两种可直接落地的实现方案,最终生成的ds2完全符合过滤后结果的要求。
方案1:LINQ to DataSet实现(推荐,适配所有新版本.NET)
这是最简洁、性能最好的实现方式,支持在逻辑中同时引用两张表的任意字段作为过滤条件,步骤如下:
- 先克隆ds1中
Customers、Orders的表结构到ds2,保留原表的字段、约束配置 - 通过LINQ做两表关联,在
where子句中写入需要的跨表过滤条件 - 遍历查询结果,将符合条件的行去重后导入ds2的对应表中
代码示例:
DataSet ds2 = new DataSet(); // 克隆原表结构到ds2 ds2.Tables.Add(ds1.Tables["Customers"].Clone()); ds2.Tables.Add(ds1.Tables["Orders"].Clone()); // 跨表查询,where子句可同时引用两张表的字段,示例条件:客户所在地为上海、订单金额大于2000、订单状态为已支付 var filterResult = from cust in ds1.Tables["Customers"].AsEnumerable() join ord in ds1.Tables["Orders"].AsEnumerable() on cust.Field<int>("CustomerId") equals ord.Field<int>("CustomerId") where cust.Field<string>("City") == "上海" && ord.Field<decimal>("TotalAmount") > 2000 && ord.Field<int>("Status") == 1 select new { CustRow = cust, OrdRow = ord }; // 导入符合条件的行,自动去重避免同一客户被多条订单重复插入 foreach (var res in filterResult) { // 检查客户是否已经导入 var custExists = ds2.Tables["Customers"].Select($"CustomerId = {res.CustRow.Field<int>("CustomerId")}").Length > 0; if (!custExists) { ds2.Tables["Customers"].ImportRow(res.CustRow); } ds2.Tables["Orders"].ImportRow(res.OrdRow); }
方案2:DataRelation关联过滤(适配.NET Framework 2.0等无LINQ的老环境)
如果你的运行环境不支持LINQ,可以通过在两表间建立内存关联关系的方式完成过滤:
- 在ds1中为
Customers和Orders添加DataRelation,绑定两表的主外键字段 - 遍历主表行,先判断主表字段是否符合过滤条件,再通过关联关系获取对应的所有子表行
- 判断子表行是否符合过滤条件,将符合要求的主、子表行导入ds2即可
代码示例:
DataSet ds2 = new DataSet(); ds2.Tables.Add(ds1.Tables["Customers"].Clone()); ds2.Tables.Add(ds1.Tables["Orders"].Clone()); // 建立两表内存关联 DataRelation relation = new DataRelation("CustOrderRel", ds1.Tables["Customers"].Columns["CustomerId"], ds1.Tables["Orders"].Columns["CustomerId"]); ds1.Relations.Add(relation); // 遍历主表做过滤 foreach (DataRow custRow in ds1.Tables["Customers"].Rows) { // 先判断主表字段条件 if (custRow.Field<string>("City") != "上海") continue; // 获取当前客户关联的所有订单 DataRow[] orderRows = custRow.GetChildRows(relation); foreach (DataRow ordRow in orderRows) { // 判断订单表字段条件 if (ordRow.Field<decimal>("TotalAmount") > 2000 && ordRow.Field<int>("Status") ==1) { // 导入客户行(去重) var custExists = ds2.Tables["Customers"].Select($"CustomerId = {custRow.Field<int>("CustomerId")}").Length >0; if (!custExists) ds2.Tables["Customers"].ImportRow(custRow); // 导入订单行 ds2.Tables["Orders"].ImportRow(ordRow); } } }
注意事项
- 不要尝试直接对DataSet对象执行SQL语句,该能力只有数据库引擎具备,离线DataSet本身不支持
- 如果必须写原生SQL实现过滤,可以临时将ds1数据导入内存数据库(如SQLite In-Memory模式),执行SQL后再Fill到ds2,但该方案性能损耗大,非极端场景不推荐
内容的提问来源于stack exchange,提问作者Navjot Singh
相关产品推荐
相关产品推荐

