如何将首查询ItemId存为标量变量传入第二个查询匹配生成数据表
问题根因
你的现有代码存在3个核心问题:
- 仅传入了单个
@ItemID参数查询修改时间,没有遍历第一个查询返回的所有ItemID,所以只能拿到单条记录的修改时间 DataTable.Merge()是行追加逻辑而非按主键关联合并列,调用后只会把第二个表的行追加到第一个表末尾,不会将修改时间拼到对应ItemID的同一行- 末尾多余的
adapter.Fill(dt)会触发二次查询,覆盖之前的合并结果
最优方案:SQL层面直接关联查询
直接把两个查询的逻辑合并为一次SQL查询,不需要在内存做二次关联,性能更高、逻辑更简单:
private const string MergedItemsQuery = @" WITH History AS ( SELECT id.ItemID, ih.ActionedDate , ROW_NUMBER() OVER(PARTITION BY id.ItemID ORDER BY ih.ActionedDate DESC) AS vn FROM AgilityItemView v JOIN ItemDetail id ON id.ItemDetailID = v.ItemDetailId JOIN ItemHistory ih ON ih.ItemDetailID = id.ItemDetailID WHERE id.[Status] in ('Released', 'Withdrawn') AND V.VersionRank = 1 AND V.[Type] = 'Document' AND ih.Action in ( 'Create','Edit','ItemCreatedByChanged' , 'ItemCustomPropertiesChanged','ItemNumberChanged','ItemRoleChanged' , 'ItemTitleChanged','Release','Restore' , 'ReturnToDraft','Revise','Withdraw' ) ) SELECT id.ItemId, id.ItemDetailID, id.Status, id.Number , id.Title, id.CreatedDate, id.WithdrawnDate , RevisionNumber = id.MajorRevisionNumber + ISNULL(id.RevisionNumberOffset, 0) , LastModifiedDate = h.ActionedDate FROM AgilityItemView v JOIN ItemDetail id ON id.ItemDetailID = v.ItemDetailId LEFT JOIN History h ON h.ItemID = id.ItemID AND h.vn = 1 WHERE id.[Status] in ('Released', 'Withdrawn') AND V.VersionRank = 1 AND V.[Type] = 'Document'";
直接执行这个查询就能拿到包含LastModifiedDate的完整结果,不需要后续内存关联。
备选方案:C#内存层面关联(适合后续需拼接多来源数据的场景)
如果你后续要从多个不同查询/接口拿字段拼接,按以下步骤实现:
- 先查询所有Item基础数据,收集所有ItemID
- 批量查询所有ItemID对应的LastModifiedDate,存入字典做匹配
- 遍历基础数据,将LastModifiedDate赋值到对应行
private DataTable GetData(string itemsQuery, string modifiedDataQuery) { using var connection = new SqlConnection(_connectionString); connection.Open(); // 第一步:查所有基础Item数据 using var itemCmd = new SqlCommand(itemsQuery, connection); var itemAdapter = new SqlDataAdapter(itemCmd); var itemDt = new DataTable(); itemAdapter.Fill(itemDt); // 给基础表加LastModifiedDate列 itemDt.Columns.Add("LastModifiedDate", typeof(DateTime)); // 第二步:收集所有ItemId,批量查修改时间 var itemIds = itemDt.AsEnumerable().Select(r => r.Field<Guid>("ItemId")).Distinct().ToList(); // 构建IN参数,避免SQL注入 var paramList = new List<SqlParameter>(); var paramPlaceholders = new List<string>(); for (int i = 0; i < itemIds.Count; i++) { var paramName = $"@ItemId{i}"; paramPlaceholders.Add(paramName); paramList.Add(new SqlParameter(paramName, itemIds[i])); } // 修改第二个查询的条件为IN var batchModifiedQuery = modifiedDataQuery.Replace("AND ItemId = @ItemID", $"AND ItemId IN ({string.Join(",", paramPlaceholders)})"); using var modifiedCmd = new SqlCommand(batchModifiedQuery, connection); modifiedCmd.Parameters.AddRange(paramList.ToArray()); var modifiedAdapter = new SqlDataAdapter(modifiedCmd); var modifiedDt = new DataTable(); modifiedAdapter.Fill(modifiedDt); // 第三步:把修改时间转成字典,匹配赋值 var modifiedDict = modifiedDt.AsEnumerable() .ToDictionary(r => r.Field<Guid>("ItemId"), r => r.Field<DateTime>("LastModifiedDate")); foreach (DataRow row in itemDt.Rows) { var itemId = row.Field<Guid>("ItemId"); if (modifiedDict.TryGetValue(itemId, out var lastModified)) { row["LastModifiedDate"] = lastModified; } } return itemDt; }
注意事项
- 批量查询如果ItemId数量超过2000,建议分批查询避免SQL参数上限
- 后续补充其他字段时,都可以用字典匹配的方式往基础DataTable里加列赋值,最后直接用这个表导出CSV即可
内容的提问来源于stack exchange,提问作者lross15
相关产品推荐
相关产品推荐

