【问题标题】:Optimize LINQ query that runs fast in Sql server?优化在 Sql server 中快速运行的 LINQ 查询?
【发布时间】:2017-02-12 11:14:18
【问题描述】:

我要计算相关表的行数:

MainTable tbl = tblInfo(id);
var count = tbl.Related_Huge_Table_Data.Count();

问题是:这需要很长时间(大约 20 秒)才能执行,尽管当我在 Sql Server 中运行此查询时,它执行不到一秒。如何在 linq 中优化此查询?我也尝试使用存储过程,但没有运气。

这是tblInfo 方法:

public MainTable tblInfo(int id)
{
    MyDataContext context = new MyDataContext();
    MainTable mt = (from c in context.MainTables
                    where c.Id == id
                    select c).SingleOrDefault();
    return mt;
}

我使用的是 LinqToSql,类是由 LinqToSql 生成的。

【问题讨论】:

  • Related_Huge_Table_Data 返回什么?
  • @GSerg 它在我的 linqtosql 中定义为 EntitySet,如下所示:public EntitySet<Related_Huge_Table_Data> Related_Huge_Table_Data
  • 如果你想要服务器端操作,你 don't want to use EntitySet
  • 你应该放弃EntitySet。按照设计,它会在处理之前将所有数据加载到内存中。
  • 正如@GSerg 所说,最好的方法是从 Child 集合开始并按 parent 过滤。另外,添加 ParentId 的索引(如果你没有)。如果您仍然有问题,那么问题就在其他地方。尝试使用此 context.Database.Log= Console.WriteLine; 查看生成的 SQL

标签: c# sql-server linq linq-to-sql


【解决方案1】:

通过运行SingleOrDefault(),您可以执行查询,然后必须在内存中处理结果。您需要一直使用IQueryable,直到您的查询完全构建。

回答“这个父记录有多少子记录”的最简单方法是从子端接近它:

using (var dx = new MyDataContext())
{
    // If you have an association between the tables defined in the context
    int count = dx.Related_Huge_Table_Datas.Where(t => t.MainTable.id == 42).Count();

    // If you don't
    int count = dx.Related_Huge_Table_Datas.Where(t => t.parent_id == 42).Count();
}

如果您坚持使用父方方法,您也可以这样做:

using (var dx = new MyDataContext())
{
    int count = dx.MainTables.Where(t => t.id == 42).SelectMany(t => t.Related_Huge_Table_Datas).Count();
}

如果你想把这个查询的一部分保留在像tblInfo这样的函数中,你可以,但是你不能从这个函数内部实例化MyDataContext,否则你会在尝试使用查询时得到一个异常与MyDataContext 的另一个实例。所以要么将MyDataContext 传递给tblInfo,要么让tblInfo 成为partial class MyDataContext 的成员:

public static IQueryable<MainTable> tblInfo(MyDataContext dx, int id)
{
    return dx.MainTables.Where(t => t.id == id);
}

...

using (var dx = new MyDataContext())
{
    int count = tblInfo(dx, 42).SelectMany(t => t.Related_Huge_Table_Datas).Count();
}

【讨论】:

  • 谢谢。我尝试了所有这些方法,但没有运气。仍然需要很长时间(大约 20 秒)
  • @user5032790 在分析器中启动跟踪,并查看当您执行 Count() 时服务器会遇到什么查询。我的看起来很合理。
【解决方案2】:

试试这个

MyDataContext context = new MyDataContext();
var count=context.Related_Huge_Table_Data.where(o=>o.Parentid==id).Count();

//or

int count=context.Database.SqlQuery<int>("select count(1) from Related_Huge_Table_Data where Parentid="+id).FirstOrDefault();

【讨论】:

    【解决方案3】:

    如果您希望充分利用 SQL 数据库的性能,直接查询它而不是使用 Linq 可能更有意义。应该更合理:)

     var Related_Huge_Table_Data = "TABLENAME";//Input table name here
     var Id = "ID"; //Input Id name here     
     var connectionString = "user id=USERNAME; password=PASSWORD server=SERVERNAME; Trusted_Connection=YESORNO; database=DATABASE; connection timeout=30";
    
     SqlCommand sCommand = new SqlCommand();
     sCommand.Connection = new SqlConnection(connectionString);
     sCommand.CommandType = CommandType.Text;
     sCommand.CommandText = $"COUNT(*) FROM {Related_Huge_Table_Name} WHERE Id={ID}";
     sCommand.Connection.Open();
    
     SqlDataReader reader = sCommand.ExecuteReader();
     var count = 0;
     if (reader.HasRows)
     {
         reader.Read();
         count = reader.GetInt32(0);
     }
     else
     {
          Debug.WriteLine("Related_Huge_Table_Data: No Rows returned in Query.");
     }
     sCommand.Connection.Close();
    

    【讨论】:

      【解决方案4】:

      试试这个:

      MyDataContext context = new MyDataContext();
      var count = context.MainTables.GroupBy(x => x.ID).Distict().Count();
      

      【讨论】:

        【解决方案5】:

        GSerg 的答案在很多情况下都是正确的。但是当你的表开始变得非常大时,即使是直接在 SQL Server 中的 Count(1) 也会很慢。

        解决此问题的最佳方法是直接查询数据库统计信息,这对于 Linq 是不可能的(或者我不知道)。

        您可以做的最好的事情是在您的表定义上创建一个静态子 (C#),它将返回以下查询的结果:

        SELECT 
            SUM(st.row_count)
        FROM 
            sys.dm_db_partition_stats st
        WHERE 
            object_name(object_id) = '{TableName}' 
            AND (index_id < 2)
        

        其中{TableName} 是您的表的数据库名称。

        请注意,这仅适用于计算表中所有记录的情况!

        【讨论】:

        • 如果您在 small、不可为空的列(例如 INT NOT NULL)上有索引,那么即使对于非常大的列,COUNT(*) 仍然非常快表.....
        • @marc_s 你能解释一下(或指向一些链接)小型非空列上的索引如何提高任意计数查询的性能吗?
        • @Evk:如果您有一个不可为空的小(窄)索引,那么该索引也将包含与实际数据一样多的条目 - 但因为它通常比整个数据记录(例如,只有 4 个字节与每条记录数百或数千个字节),扫描此索引将花费更少的数据页来加载,因此完成时间也更少
        【解决方案6】:

        您的 linq2sql 是返回记录集,然后在本地执行 .Count(),还是将 SQL 发送到服务器以在服务器上进行计数?那里的性能会有很大差异。

        另外,您是否检查过执行查询时生成的 SQL?从内存中,Linq2Sql 允许您检查 SQL(也许通过在您的类上设置记录器?)。在Entity Framework中,调试和检查IQueryable对象时可以看到,不知道Linq2Sql中是否有等价物。

        Way to view SQL executed by LINQ in Visual Studio?

        或者,使用 SQL Server Profiler(如果可用),或者以某种方式查看正在执行的内容。

        【讨论】:

          【解决方案7】:

          您可以尝试以下方法:-

            var c = from rt in context.Related_Huge_Table_Data
              join t in context.MainTables
              on rt.MainTableId ==t.id where t.id=id
              select new {rt.id};
          
             var count=c.Distict().Count();
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多