这是一个与C# MVC2 Jqgrid - what is the correct way to do server side paging?相关的问题我在那里询问并找到了如何提高对具有 2000 行左右的表的查询的性能。性能从 10 秒提高到 1 秒。
现在我正在尝试执行完全相同的查询,其中表有 20,000 行 - 查询需要 30 秒。我怎样才能进一步改进它? 2 万行仍然不是一个很大的数字。
我有一些可能的想法是:
- 可以通过去规范化去除连接和求和来改进
- 创建一个 View 并查询,而不是查询和连接表
- 不要查询整个表,让用户先选择一些过滤器(例如 A | B | C .. 等过滤器)
- 为表添加更多索引
- 还有别的吗?
这是 2 万行需要 30 秒的 MVC 操作:(参数由 jqgrid 提供,其中 sidx = 排序列,sord = 排序顺序)
public ActionResult GetProductList(int page, int rows, string sidx, string sord,
string searchOper, string searchField, string searchString)
{
if (sidx == "Id") { sidx = "ProductCode"; }
var pagedData = _productService.GetPaged(sidx, sord, page, rows);
var model = (from p in pagedData.Page<Product>()
select new
{
p.Id, p.ProductCode, p.ProductDescription,
Barcode = p.Barcode ?? string.Empty,
UnitOfMeasure = p.UnitOfMeasure != null ? p.UnitOfMeasure.Name : "",
p.PackSize,
AllocatedQty = p.WarehouseProducts.Sum(wp => wp.AllocatedQuantity),
QtyOnHand = p.WarehouseProducts.Sum(wp => wp.OnHandQuantity)
});
var jsonData = new
{
Total = pagedData.TotalPages, Page = pagedData.PageNumber,
Records = pagedData.RecordCount, Rows = model
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
ProductService.GetPaged() 调用 ProductRepository.GetPaged,后者调用 genericRepository.GetPaged() 执行此操作:
public ListPage GetPaged(string sidx, string sord, int page, int rows)
{
var list = GetQuery().OrderBy(sidx + " " + sord);
int totalRecords = list.Count();
var listPage = new ListPage
{
TotalPages = (totalRecords + rows - 1) / rows,
PageNumber = page,
RecordCount = totalRecords,
};
listPage.SetPageData(list
.Skip((page > 0 ? page - 1 : 0) * rows)
.Take(rows).AsQueryable());
return listPage;
}
.OrderBy() 子句使用 LinqExtensions 以便我可以传入字符串而不是谓词 - 这会减慢速度吗?
最后ListPage只是一个类,方便包装jqgrid分页需要的属性:
public class ListPage
{
private IQueryable _data;
public int TotalPages { get; set; }
public int PageNumber { get; set; }
public int RecordCount { get; set; }
public void SetPageData<T>(IQueryable<T> data)
{
_data = data;
}
public IQueryable<T> Page<T>()
{
return (IQueryable<T>)_data;
}
}
GetQuery 是:
public IQueryable<T> GetQuery()
{
return ObjectSet.AsQueryable();
}
自定义 .OrderBy 方法由以下两个方法组成:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source,
string ordering, params object[] values)
{
return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values);
}
public static IQueryable OrderBy(this IQueryable source, string ordering,
params object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (ordering == null) throw new ArgumentNullException("ordering");
ParameterExpression[] parameters = new ParameterExpression[] {
Expression.Parameter(source.ElementType, "") };
ExpressionParser parser = new ExpressionParser(parameters, ordering, values);
IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering();
Expression queryExpr = source.Expression;
string methodAsc = "OrderBy";
string methodDesc = "OrderByDescending";
foreach (DynamicOrdering o in orderings)
{
queryExpr = Expression.Call(
typeof(Queryable), o.Ascending ? methodAsc : methodDesc,
new Type[] { source.ElementType, o.Selector.Type },
queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters)));
methodAsc = "ThenBy";
methodDesc = "ThenByDescending";
}
return source.Provider.CreateQuery(queryExpr);
}
最佳答案
让我担心的一点是:
.Take(rows).AsQueryable()
您需要添加 AsQueryable()
的事实向我建议它目前是IEnumerable<T>
,这意味着您可能在查询的错误端执行分页(通过网络返回方式太多数据)。没有GetQuery()
和习俗 OrderBy()
很难确定 - 但一如既往,要做的第一件事是通过跟踪分析查询。查看执行了什么查询以及返回了什么数据。 EFProf可能会使这变得容易,但 SQL 跟踪可能就足够了。
关于C# Entity Framework + Linq - 如何加速慢速查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4231011/