C# 从大型数据库检索行时灵活的Linq到实体标准的性能优化

C# 从大型数据库检索行时灵活的Linq到实体标准的性能优化,c#,sql-server,entity-framework,linq-to-entities,C#,Sql Server,Entity Framework,Linq To Entities,我有一个包含数十亿行的数据库。 我创建了一个函数,该函数接收用户数量的参数,并通过这些参数切割数据库。 这对我处理小数据库(30000行)效果很好,但当我尝试在大数据库上使用此函数时,我从SQLSERVER获得了TIMEOUTEXCEPTION 这是我的密码: public static IQueryable<LogViewer.EF.InternetEF.Log> ExecuteInternetGetLogsQuery(FilterCriteria p_Criteria, ref

我有一个包含数十亿行的数据库。 我创建了一个函数,该函数接收用户数量的参数,并通过这些参数切割数据库。 这对我处理小数据库(30000行)效果很好,但当我尝试在大数据库上使用此函数时,我从SQLSERVER获得了
TIMEOUTEXCEPTION

这是我的密码:

public static IQueryable<LogViewer.EF.InternetEF.Log> ExecuteInternetGetLogsQuery(FilterCriteria p_Criteria, ref GridView p_Datagrid)
{
    IQueryable<LogViewer.EF.InternetEF.Log> internetQuery = null;

    using (InternetDBConnectionString context = new InternetDBConnectionString())
    {
        internetQuery = context.Logs;
        if ((p_Criteria.DateTo != null && p_Criteria.DateFrom != null))
        {
            internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom && c.Timestamp < p_Criteria.DateTo);
        }
        else if (p_Criteria.DateFrom != null && p_Criteria.DateFrom > DateTime.MinValue)
        {
            internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom);
        }
        else if (p_Criteria.DateTo != null && p_Criteria.DateTo > DateTime.MinValue)
        {
            internetQuery = internetQuery.Where(c => c.Timestamp < p_Criteria.DateTo);
        }
        if (!string.IsNullOrEmpty(p_Criteria.FreeText))
        {
            internetQuery = internetQuery.Where(c => c.FormattedMessage.Contains(p_Criteria.FreeText));
        }

        if (p_Criteria.Titles.Count > 0)
        {
            internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Titles.Contains(c.Title)).AsQueryable();
        }
        if (p_Criteria.MachineNames.Count > 0)
        {
            internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.MachineNames.Contains(c.MachineName)).AsQueryable();
        }
        if (p_Criteria.Severities.Count > 0)
        {
            internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Severities.Contains(c.Severity)).AsQueryable();
        }
        internetQuery= internetQuery.OrderByDescending(c=>c.LogID);
        if (internetQuery.Count() > p_Criteria.TopValue)
        {
            internetQuery = internetQuery.Take(p_Criteria.TopValue);
        }
        p_Datagrid.DataSource = internetQuery;
        p_Datagrid.DataBind();
        return internetQuery;

    }  
}
public static IQueryable ExecuteInternetGetLogsQuery(FilterCriteria p_Criteria,ref GridView p_Datagrid)
{
IQueryable internetQuery=null;
使用(InternetDBConnectionString上下文=新的InternetDBConnectionString())
{
internetQuery=context.Logs;
if((p_Criteria.DateTo!=null&&p_Criteria.DateFrom!=null))
{
internetQuery=internetQuery.Where(c=>c.Timestamp>=p_-Criteria.DateFrom&&c.TimestampDateTime.MinValue)
{
internetQuery=internetQuery.Where(c=>c.Timestamp>=p_Criteria.DateFrom);
}
else if(p_Criteria.DateTo!=null&&p_Criteria.DateTo>DateTime.MinValue)
{
internetQuery=internetQuery.Where(c=>c.Timestampc.FormattedMessage.Contains(p_Criteria.FreeText));
}
如果(p_Criteria.Titles.Count>0)
{
internetQuery=internetQuery.AsEnumerable()。其中(c=>p_Criteria.Titles.Contains(c.Title)).AsQueryable();
}
如果(p_Criteria.MachineNames.Count>0)
{
internetQuery=internetQuery.AsEnumerable()。其中(c=>p_Criteria.MachineNames.Contains(c.MachineName)).AsQueryable();
}
如果(p_标准.严重性.计数>0)
{
internetQuery=internetQuery.AsEnumerable()。其中(c=>p_Criteria.Severity.Contains(c.Severity)).AsQueryable();
}
internetQuery=internetQuery.OrderByDescending(c=>c.LogID);
if(internetQuery.Count()>p_Criteria.TopValue)
{
internetQuery=internetQuery.Take(p_Criteria.TopValue);
}
p_Datagrid.DataSource=internetQuery;
p_Datagrid.DataBind();
返回internetQuery;
}  
}
我的SQL版本是2005。 我在
p_Datagrid.DataBind()上遇到了一个异常

有什么建议吗?
谢谢

据我所知,您有以下选项:

  • 增加超时时间(坏idé只会在将来移动问题)
  • 而不是执行linq查询。通过存储过程获取数据
  • 制作网格页面。所以您只需检索目标页面的数据
  • 查看查询计划,查看是否可以对正在执行
    where
    语句和
    orderby
    的列执行任何索引
  • 为什么在数据网格中需要有数十亿行。要求是什么?也许您可以只显示
    TOP1000
    TOP10000
    。因为从一个用户的角度来看,我看不出看到一个10亿行的网格有什么好处
那只是我的想法

编辑

如果我有这个功能,我会开始看这段代码:

if (p_Criteria.Titles.Count > 0)
{
     internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Titles.Contains(c.Title)).AsQueryable();
}
if (p_Criteria.MachineNames.Count > 0)
{
      internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.MachineNames.Contains(c.MachineName)).AsQueryable();
}
if (p_Criteria.Severities.Count > 0)
{
      internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Severities.Contains(c.Severity)).AsQueryable();
}

这实际上是对结果进行IEnumerable,然后对数据库调用执行内存中的
where
语句。这样做也可能会有问题,因为当您调用相关表时,它会调用数据库。也许您可以获取行,然后使用
IQueryable
的id执行
包含的
。使用
IQueryable
尿布的所有好处。

由于具体方案不可用,您可以尝试以下方法

  • 编写一个包含所有筛选条件的存储过程,并从代码中发送参数。然后从代码中执行存储过程,并检查是否仍有超时。要检查如何从实体框架调用SP,请阅读

  • 如果第1步没有成功。您可能需要检查表设计并添加索引和/或额外的过滤器。检查有关如何为SQL Server数据库编制索引的指南的步骤

  • 您可能还希望创建表的“卷影”副本,以保留存档的DB行。对于归档,我指的是到目前为止没有任何用处,但无法永久删除的行


  • 编辑:我同意@Arion关于使用分页网格而不是获取所有行的观点。

    一般来说,像这样的“瑞士军刀”规范或标准模式很难优化(即SQL级别的索引),因为客户机/用户可以指定大量的过滤器组合排列。因此,如果您可以以某种方式强制用户指定至少一个强制标准,这将显著减少行数,例如,通过强制日期范围且不超过一个月,我将从那里开始,因为这样,当我们查看索引时,至少我们有了一些开始

    由于行数可能很大,我会断言或验证用于限制行数的
    p_Criteria.TopValue
    的值始终存在,并且是一个合理的数字,例如
    Take(1000)
    。如果达到此阈值,您始终可以警告用户缩小其搜索范围

    主要的问题可能是对
    标题
    机器名
    严重性
    进行过滤时,每个调用都会调用
    AsEnumerable()
    ,这会具体化查询,因此您在内存中而不是在SQL中对这3个过滤器进行评估,可能会有大量记录。所有最新版本的EF都能够将形式为
    Where(c=>IEnumerable.Contains(c.Column))
    的谓词转换为SQL
    Where c.Column IN(X1,X2,X3)

    i、 你应该
        if (p_Criteria.Titles.Any())
        {
            internetQuery = internetQuery
                .Where(c => p_Criteria.Titles.Contains(c.Title));
        }
        if (p_Criteria.MachineNamesAny())
        {
            internetQuery = internetQuery
                .Where(c => p_Criteria.MachineNames.Contains(c.MachineName));
        }
        if (p_Criteria.Severities.Any())
        {
            internetQuery = internetQuery
                .Where(c => p_Criteria.Severities.Contains(c.Severity));
        }
    
    internetQuery = internetQuery.Take(p_Criteria.TopValue);
    
    if (!string.IsNullOrEmpty(p_Criteria.FreeText))
    {
        internetQuery = internetQuery
            .Where(c => c.FormattedMessage.StartsWith(p_Criteria.FreeText));
    }
    
    if (p_Criteria.DateFrom != null && p_Criteria.DateFrom > DateTime.MinValue)
    {
        internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom);
    }
    if (p_Criteria.DateTo != null && p_Criteria.DateTo > DateTime.MinValue)
    {
        internetQuery = internetQuery.Where(c => c.Timestamp < p_Criteria.DateTo);
    }
    
    internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Severities.Contains(c.Severity))
    
    public static IQueryable<LogViewer.EF.InternetEF.Log> ExecuteInternetGetLogsQuery(FilterCriteria p_Criteria, ref GridView p_Datagrid)
            {
    
                
                IQueryable<LogViewer.EF.InternetEF.Log> internetQuery = null;
                List<LogViewer.EF.InternetEF.Log> executedList = null;
                using (InternetDBConnectionString context = new InternetDBConnectionString())
                {
                    internetQuery = context.Logs;
                    if ((p_Criteria.DateTo != null && p_Criteria.DateFrom != null))
                    {
                        internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom.Value && c.Timestamp < p_Criteria.DateTo.Value);
                    }
                    else if (p_Criteria.DateFrom != null && p_Criteria.DateFrom > DateTime.MinValue)
                    {
                        internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom);
                    }
                    else if (p_Criteria.DateTo != null && p_Criteria.DateTo > DateTime.MinValue)
                    {
                        internetQuery = internetQuery.Where(c => c.Timestamp < p_Criteria.DateTo);
                    }
                    if (!string.IsNullOrEmpty(p_Criteria.FreeText))
                    {
                        internetQuery = internetQuery.Where(c => c.FormattedMessage.Contains(p_Criteria.FreeText));
                    }
    
                    
                    if (p_Criteria.Titles.Count > 0)
                    {
                        internetQuery = internetQuery.Where(BuildOrExpression<LogViewer.EF.InternetEF.Log, string>(p => p.Title, p_Criteria.Titles));
                    }
                    if (p_Criteria.MachineNames.Count > 0)
                    {
                        internetQuery = internetQuery.Where(BuildOrExpression<LogViewer.EF.InternetEF.Log, string>(p => p.MachineName, p_Criteria.MachineNames));
                    }
                    if (p_Criteria.Severities.Count > 0)
                    {
                        internetQuery = internetQuery.Where(BuildOrExpression<LogViewer.EF.InternetEF.Log, string>(p => p.Severity, p_Criteria.Severities));
                    }
    
    
                    internetQuery = internetQuery.Take(p_Criteria.TopValue);
                    executedList = internetQuery.ToList<LogViewer.EF.InternetEF.Log>();
                    executedList = executedList.OrderByDescending(c => c.LogID).ToList<LogViewer.EF.InternetEF.Log>(); ;
    
                    p_Datagrid.DataSource = executedList;
    
                    p_Datagrid.DataBind();
    
    
                    return internetQuery;
    
                }
            }
    
    
    
    public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(
            Expression<Func<TElement, TValue>> valueSelector,
            IEnumerable<TValue> values )
            {
                if (null == valueSelector)
                    throw new ArgumentNullException("valueSelector");
    
                if (null == values)
                    throw new ArgumentNullException("values");
    
                ParameterExpression p = valueSelector.Parameters.Single();
    
                if (!values.Any())
                    return e => false;
    
                var equals = values.Select(value =>
                    (Expression)Expression.Equal(
                         valueSelector.Body,
                         Expression.Constant(
                             value,
                             typeof(TValue)
                         )
                    )
                );
    
                var body = equals.Aggregate<Expression>(
                         (accumulate, equal) => Expression.Or(accumulate, equal)
                 );
    
                return Expression.Lambda<Func<TElement, bool>>(body, p);
            }