C# 如何优化LINQ DB请求?
我有下一个代码,取DB中元素的最后一个和前最后一个参数:C# 如何优化LINQ DB请求?,c#,linq,C#,Linq,我有下一个代码,取DB中元素的最后一个和前最后一个参数: var result = _context.Contacts.Where(conts => conts.CreatorUserId == _userManager.GetUserId(this.User)) .Select(conts => new { conts.ID, conts.Mobile, conts.Email, cont
var result = _context.Contacts.Where(conts => conts.CreatorUserId == _userManager.GetUserId(this.User))
.Select(conts => new
{
conts.ID,
conts.Mobile,
conts.Email,
conts.Facebook,
conts.StateId,
conts.CreatorUserId,
conts.Birthday,
conts.Description,
conts.Name,
conts.Photo,
conts.SecondName,
Tags = conts.Tags.Select(d=> d.UserTag.Emoji),
NpaId = conts.NpaInfo.NpaId,
PartnerPvPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.MyPV).FirstOrDefault(),
GroupPvPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.GroupPV).FirstOrDefault(),
LevelPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault(),
PartnerPv = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Take(1).Select(x => x.MyPV).FirstOrDefault(),
Level = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault(),// conts.NpaInfo.PerformanceBonusLevelId,
GroupPv = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Take(1).Select(x => x.GroupPV).FirstOrDefault(),
EntryDate = conts.NpaInfo.EntryDate,
ExpirationDate = conts.NpaInfo.ExpirationDate
});
事实上,该部分:
PartnerPvPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.MyPV).FirstOrDefault(),
GroupPvPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.GroupPV).FirstOrDefault(),
LevelPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault()
在1个请求中转换为类似的内容(仅显示一个块的部分):
我不想重复同一部分多次:
conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1)
所以,如果我喜欢这个:
PrevBuffer = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(
param => new
{
param.MyPV,
param.GroupPV,
param.PerformanceBonusLevelId
}
),
它将在输出中为每行显示许多单独的请求,如:
SELECT [x0].[MyPV], [x0].[GroupPV], [x0].[PerformanceBonusLevelId]
FROM [NpaDatas] AS [x0]
WHERE @_outer_ID1 = [x0].[NpaInfoId]
ORDER BY [x0].[DataMonth] DESC
OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
这一部分也是如此:
Tags = conts.Tags.Select(d=> d.UserTag.Emoji)
将获得多个选择,如:
SELECT [d.UserTag].[Emoji]
FROM [ContactTags] AS [d]
LEFT JOIN [UserTags] AS [d.UserTag] ON [d].[UserTagId] = [d.UserTag].[ID]
WHERE @_outer_ID = [d].[ContactId]
是否可以对其进行优化?您应该使用GroupBy:
class Program
{
static void Main(string[] args)
{
Conts conts = new Conts();
List<NpaDatas> data = conts.NpaInfo.NpaDatas
.OrderByDescending(x => x.DataMonth)
.GroupBy(x => x.DataMonth).Select(x => x.FirstOrDefault()).ToList();
//To get the second latest use followng
NPaDatas results = data.Skip(1).FirstOrDefault();
}
}
public class NpaDatas
{
public string NpaInfoId { get; set; }
public DateTime DataMonth { get; set; }
public PV PartnerPv { get; set; }
public PV PerformanceBonusLevelId { get; set; }
public PV GroupPv { get; set; }
}
public class PV
{
//data not specified
}
public class Conts
{
public NpaInfo NpaInfo { get; set; }
}
public class NpaInfo
{
public string ID { get; set; }
public List<NpaDatas> NpaDatas { get; set; }
}
类程序
{
静态void Main(字符串[]参数)
{
Conts Conts=新Conts();
列表数据=conts.NpaInfo.npadass
.OrderByDescending(x=>x.DataMonth)
.GroupBy(x=>x.DataMonth)。选择(x=>x.FirstOrDefault()).ToList();
//获取以下第二个最新用法
NPADASS结果=data.Skip(1).FirstOrDefault();
}
}
公共类数据
{
公共字符串NpaInfoId{get;set;}
公共日期时间数据月{get;set;}
公共PV PartnerPv{get;set;}
公共PV PerformanceBonusLevelId{get;set;}
公共PV组PV{get;set;}
}
公共级光伏
{
//未指定数据
}
公共类Conts
{
公共NpaInfo NpaInfo{get;set;}
}
公共类NpaInfo
{
公共字符串ID{get;set;}
公共列表数据集{get;set;}
}
这样做将显著改进查询
var result = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Take(2).ToArray();
PartnerPvPrev = result.Skip(1).Take(1).Select(x => x.MyPV).FirstOrDefault();
GroupPvPrev = result.Skip(1).Take(1).Select(x => x.GroupPV).FirstOrDefault();
LevelPrev = result.Skip(1).Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault();
PartnerPv = result.Take(1).Select(x => x.MyPV).FirstOrDefault();
Level = result.Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault();
GroupPv = result.Take(1).Select(x => x.GroupPV).FirstOrDefault();
您应该首先选择重复零件,并在后续查询中使用这些零件。使用
let
关键字,这在查询语法中要容易得多:
var result = from conts in _context.Contacts
where conts.CreatorUserId == _userManager.GetUserId(this.User)
let monthsSorted = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth)
let firstMonth = monthsSorted.FirstOrDefault()
let prevMonth = monthsSorted.Skip(1).FirstOrDefault()
select new
{
conts.ID,
conts.Mobile,
conts.Email,
conts.Facebook,
conts.StateId,
conts.CreatorUserId,
conts.Birthday,
conts.Description,
conts.Name,
conts.Photo,
conts.SecondName,
Tags = conts.Tags.Select(d=> d.UserTag.Emoji),
NpaId = conts.NpaInfo.NpaId,
PartnerPvPrev = prevMonth.MyPV,
GroupPvPrev = prevMonth.GroupPV,
LevelPrev = prevMonth.PerformanceBonusLevelId,
PartnerPv = firstMonth.MyPV,
GroupPv = firstMonth.GroupPV,
Level = firstMonth.PerformanceBonusLevelId,
EntryDate = conts.NpaInfo.EntryDate,
ExpirationDate = conts.NpaInfo.ExpirationDate
};
而不是两个查询<代码>跳过(1)。执行(1),然后稍后执行
Take(1)
您可以尝试组合到Take(2)
的单个查询,然后执行逻辑客户端(可能存在一个Take(2)
解决方案,这都是数据库端,我不知道)。。。这将使您的查询总数减少一半,至少您真的应该显示一个。您粘贴的代码似乎是new
匿名对象创建的一部分。所有的数据库工作都是在此之外进行的,这将是我们优化查询所需要看到的。更新了完整版本的代码。但是ToArray()将获取NpaData的所有字段,我只需要其中6个字段(Last的3个和Prev的3个)。在您的情况下,如果我可以像:result[0]这样索引值,为什么还要在这里再次选择。MyPv@user2377299-然后选择.ToArray()
之前的三个字段。为了简单起见,我保留了原始代码。@Enigmativity您能解释一下它如何比所讨论的解决方案本身更好吗?.ToArray()
强制执行查询,以便只向数据库发送一个查询。接下来的6行都是从内存中的数据开始工作的。对不起,可能有一些误解。NpaInfo有多个NpaData。每个NpaData都有级别、PartnerPv、GrouPv。我需要取最后和最后之前的物品。NpaData不包含这6个字段。我根据您发布的SQL更新了代码,但您的SQL看起来不正确。我不明白你为什么需要筛选ID。我怀疑你应该有一个列表,每个列表都应该只有相同的ID。此外,发布的SQL有额外的右括号,这意味着你没有发布整个SQL。联系人有一个NpaInfo。NpaInfo有许多日期不同的NpaData。我需要从当前用户创建的联系人的NpaInfo的每个NpaData(上一个和上一个)中提取3个参数。SQL-只对重复的问题部分进行了短路。因此,您与我确认NpaInfo和NAPDATA的ID将相同,因此您不需要筛选ID?我的查询有什么问题吗?NpaInfo.ID!=NapData.ID-两个不同的对象。我在哪里按ID过滤?这6个值中的每一个都有多个选择。比如:从[NpaDatas]中选择[x].[MyPV]作为[x],其中[conts.NpaInfo].[ID]=[x].[NpaInfoId]按[x].[DataMonth]排序。[DataMonth]DESC OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLYOK,显然这是LINQ to SQL(请始终使用特定的标记)。在实体框架中,这将产生重大影响。恐怕您唯一的选择是不在一个查询中收集所有数据,因为LINQ to SQL在执行此操作时总是效率低下。我已经对此进行了再次研究,实际上,这是您唯一的选择--或者您最初的查询,但至少我的查询更易于阅读。使用LINQ to SQL,您将始终根据希望在结果中包含的conts.NpaInfo.NpaData
属性获得子查询。我已经研究了获取2个conts.NpaInfo.NpaData
项的集合(Take(2)
)并在以后将结果展平的替代方法,但这总是会产生N+1个查询。如果当前查询的执行情况合理,请继续执行,否则请找到另一种方法来收集数据。
var result = from conts in _context.Contacts
where conts.CreatorUserId == _userManager.GetUserId(this.User)
let monthsSorted = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth)
let firstMonth = monthsSorted.FirstOrDefault()
let prevMonth = monthsSorted.Skip(1).FirstOrDefault()
select new
{
conts.ID,
conts.Mobile,
conts.Email,
conts.Facebook,
conts.StateId,
conts.CreatorUserId,
conts.Birthday,
conts.Description,
conts.Name,
conts.Photo,
conts.SecondName,
Tags = conts.Tags.Select(d=> d.UserTag.Emoji),
NpaId = conts.NpaInfo.NpaId,
PartnerPvPrev = prevMonth.MyPV,
GroupPvPrev = prevMonth.GroupPV,
LevelPrev = prevMonth.PerformanceBonusLevelId,
PartnerPv = firstMonth.MyPV,
GroupPv = firstMonth.GroupPV,
Level = firstMonth.PerformanceBonusLevelId,
EntryDate = conts.NpaInfo.EntryDate,
ExpirationDate = conts.NpaInfo.ExpirationDate
};