You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#从SQL Server加载20万条记录到字典的最优性能方案

优化方案:提升嵌套字典构建性能

原代码的核心性能瓶颈在于用FirstOrDefault遍历List查找子项(时间复杂度O(n)),加上代码里的逻辑错误导致重复创建/添加对象,进一步拖慢了处理速度。以下是针对性的优化措施:


关键优化点

  • 用Dictionary替代List存储子项:将AgentRecord.Locations改为Dictionary<int, LocationSF>(以LocIdSeq为键),LocationSF.Licenses改为Dictionary<int, LicenseSF>(以LicenseId为键),查找操作从O(n)降为O(1)。
  • 提前初始化集合:在类的构造函数中直接初始化字典/集合,避免每次判断null的开销。
  • 修正逻辑错误:原代码中存在多处字段匹配错误(比如用cusId匹配LocIdSeq、从agentSF而非locSF找许可证),这些错误会导致重复创建大量冗余对象,必须修正。
  • 减少不必要的类型转换:LicenseId本身是int类型,无需转成string存储,直接用int作为键和属性类型,节省转换开销。
  • 简化DBNull判断:直接使用SqlDataReader.IsDBNull,避免Convert.IsDBNull的额外调用。

优化后的代码实现

修正后的实体类

public class LicenseSF
{
    public int LicenseId { get; set; }
}

public class LocationSF
{
    public int LocIdSeq { get; set; }
    // 用Dictionary存储许可证,查找更快
    public Dictionary<int, LicenseSF> Licenses { get; set; } = new Dictionary<int, LicenseSF>();
}

public class AgentRecord
{
    public int CusIdSeq { get; set; }
    // 用Dictionary存储地点,查找更快
    public Dictionary<int, LocationSF> Locations { get; set; } = new Dictionary<int, LocationSF>();
}

数据读取与字典构建代码

Dictionary<int, AgentRecord> agencyDict = new Dictionary<int, AgentRecord>();

using (var con = new SqlConnection(connectionString))
{
    using (var cmd = new SqlCommand(sql, con))
    {
        await con.OpenAsync();
        using (var reader = await cmd.ExecuteReaderAsync())
        {
            // 提前获取列索引,避免每次读取时重复查找列位置
            var cusIdIdx = reader.GetOrdinal("CusIdSeq");
            var locIdSeqIdx = reader.GetOrdinal("LocIdSeq");
            var licenseIdIdx = reader.GetOrdinal("LicenseId");
            var cusNameIdx = reader.GetOrdinal("CUS_Name");

            while (await reader.ReadAsync())
            {
                // 读取核心字段,简化DBNull判断
                int cusId = reader.IsDBNull(cusIdIdx) ? -1 : reader.GetInt32(cusIdIdx);
                int locIdSeq = reader.IsDBNull(locIdSeqIdx) ? -1 : reader.GetInt32(locIdSeqIdx);
                int licenseId = reader.IsDBNull(licenseIdIdx) ? -1 : reader.GetInt32(licenseIdIdx);
                string cusName = reader.IsDBNull(cusNameIdx) ? string.Empty : reader.GetString(cusNameIdx);

                // 获取或创建Agent
                if (!agencyDict.TryGetValue(cusId, out var agentSF))
                {
                    agentSF = new AgentRecord { CusIdSeq = cusId };
                    agencyDict[cusId] = agentSF;
                }

                // 处理地点(仅当LocIdSeq有效时)
                if (locIdSeq > 0)
                {
                    // 获取或创建Location
                    if (!agentSF.Locations.TryGetValue(locIdSeq, out var locSF))
                    {
                        locSF = new LocationSF { LocIdSeq = locIdSeq };
                        agentSF.Locations[locIdSeq] = locSF;
                    }

                    // 处理许可证(仅当LicenseId有效时)
                    if (licenseId > 0)
                    {
                        // 获取或创建License
                        if (!locSF.Licenses.TryGetValue(licenseId, out var licSF))
                        {
                            licSF = new LicenseSF { LicenseId = licenseId };
                            locSF.Licenses[licenseId] = licSF;
                        }
                    }
                }
            }
        }
    }
}

额外性能建议

  • 优化SQL查询:26个左连接会返回大量冗余数据(笛卡尔积),可以考虑将查询拆分为3个独立的查询(客户、地点、许可证),分别读取后在内存中关联,减少返回的总行数,降低网络传输和内存开销。
  • 启用CommandBehavior.SequentialAccess:如果不需要随机访问列,可以在ExecuteReaderAsync中添加CommandBehavior.SequentialAccess,减少内存占用,提升读取速度。
  • 实体类优化:如果不需要变更属性,可以将实体类改为readonly struct(值类型),减少GC压力。

内容的提问来源于stack exchange,提问作者WorkJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:37:03