【问题标题】:Linq-to-Sql Sproc -- ToList() is Too SlowLinq-to-Sql Sproc -- ToList() 太慢了
【发布时间】:2013-05-01 23:37:46
【问题描述】:

我正在使用 Linq2Sql 来返回存储过程的结果。 sproc 在 2 秒内提供 100,000 条记录。应用 ToList() 需要 2 分钟以上。

该项目是一个 ASP.NET WebForm。在代码隐藏中,我试图从事务系统中获取记录,以对仪表板类型的报告应用各种分析。 10 万条记录是平均一个月的数据量。使用较小的数据一切正常。

using (FooDataContext dbml = new FooDataContext())
{
    var query = dbml.FooBar(OneParam, TwoParam, ThreeParam);
    //no delay

    var results = query.ToList();
    //takes over 2 minutes -- consistent network traffic throughout

    ReportGenerator.PivotTable(results);
    ReportGenerator.Chart(results);
    //etc.
}

我使用 ToList() 来利用 Linq 的水合 sproc 对象,这些对象对于使用 lambda 表达式评估结果非常方便。

但是 ToList() 需要非常非常长的时间来为这么多数据构建每个结果。如果我在那段时间暂停该过程,我可以看到它只是一遍又一遍地循环通过 sproc 的构造函数。查看我的网络流量似乎可以确认每个对象的代码都将返回到数据库。将 DeferredLoadingEnabled 设置为 false 没有帮助。

有趣的是,我认为存储过程的一个缺点是它们会立即将所有数据转储给您,而不是作为 IQueryable?

【问题讨论】:

  • 我不认为框架可以延迟加载存储过程结果。每条记录有多大?
  • 存储过程在 LINQ 之外运行需要多长时间?
  • "sproc 在 2 秒内提供 100,000 条记录" 在什么情况下?通过 SSMS 或其他查询工具运行时? FooBar 返回什么类型?该类型的构造函数是否在做任何可能减慢速度的事情?
  • 如果您的网络流量持续不断,实际上可能需要很长时间才能通过网络传输 100,000 行的所有记录数据。

标签: c# asp.net sql-server-2008 linq-to-sql stored-procedures


【解决方案1】:

我认为您正在尝试以非最佳方式解决问题。如果您想提供仪表板报告,您应该有后台进程(可能是 sql 代理作业或 Windows 服务)构建物化仪表板表,其中将数字压缩成更小的“报告 dto”,然后您可以查询并放入仪表板。我不在乎 SQL 是什么,每个请求提取 10 万条记录,然后执行一些计算/处理以以有意义的方式显示该数据将是浪费且执行速度较慢。

【讨论】:

  • 我不反对,坦率地说,我担心可能是这种情况,但我尝试做的建模更适合 C# 的程序能力,而不是 T-SQL 的集合-based operations....在服务器端处理高保真数据更容易,更强大,更易于维护,所以我希望找到一个解决方案。但也许 L2S 处理的太多了。
  • 相信我,我的回答是基于我自己努力做到这一点的努力。在尝试调整请求超时、客户期望、不断变化的生产数据记录计数等无休止的猫捉老鼠之后,我刚刚得出结论,在后台进程中执行此操作是最好的方法。您不必在 SQL 中执行此操作,您可以在另一个进程(Windows 服务或计划任务控制台应用程序)中处理 C# 中的数据,然后将该处理的输出保存在专门用于仪表板 Web 请求的表中.
【解决方案2】:

在获取记录时尝试执行 TOList()。这可能会很快奏效。 var query = dbml.FooBar(OneParam, TwoParam, ThreeParam).Tolist(); 直接用收藏就好了

 ReportGenerator.PivotTable(query);
 ReportGenerator.Chart(query);

【讨论】:

  • 一行而不是两行不会改变任何事情。
【解决方案3】:

我的解决方案是使用老式 ADO.NET 来查询存储过程。显然不像 L2S 那样简单,但在这种情况下,速度比 L2S 快得多,它似乎为每一行运行该过程。

public List<object> Foo(SqlConnection connection)
{
    var query = "[dbo].[FooBar]";
    var command = new SqlCommand(query, connection);
    command.CommandType = CommandType.StoredProcedure;
    connection.Open();

    var reader = command.ExecuteReader();
    var results = new List<object>(); // Or whatever type your data is.

    while (reader.Read())
    {
        // Make this work for your particular data structure:
        results.Add(reader.GetString(0));
    }

    connection.Close();

    return results;
}

为了安全起见,请务必使用 using 语句创建连接:

using (var connection = new SqlConnection(connectionString))
{
    results = Foo(connection);
}

ReportGenerator.PivotTable(results);
ReportGenerator.Chart(results);
//etc.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多