我是Linq的新手,我正在尝试优化一些查询,我不知道这些查询是否可能:
var cSRez = (from l in MyTable
where l.Name == "dcc" && l.Service == "Main"
orderby l.Time descending
select l.Value).FirstOrDefault();
var cDRez = (from l in MyTable
where l.Name == "dcc" && l.Service == "DB"
orderby l.Time descending
select l.Value).FirstOrDefault();
var dSRez = (from l in MyTable
where l.Name == "ddc" && l.Service == "Main"
orderby l.Time descending
select (long?)l.Value).FirstOrDefault();
var dDRez = (from l in MyTable
where l.Name == "ddc" && l.Service == "DB"
orderby l.Time descending
select (long?)l.Value).FirstOrDefault();
var mSRez = (from l in MyTable
where l.Name == "mc" && l.Service == "Main"
orderby l.Time descending
select l.Value).FirstOrDefault();
var mDRez = (from l in MyTable
where l.Name == "mc" && l.Service == "DB"
orderby l.Time descending
select l.Value).FirstOrDefault();
成为一个单一的。我在考虑row_number() over(partition by...
(SQL),但我认为这不是最好的想法。
可以将这六个单独的查询折叠成一个查询吗?
您需要对名称和服务进行分组,然后对所需的特定对进行过滤,然后从第一个匹配项中选择名称和服务以及值。请注意,任何不存在的对都不会在结果中表示,并且在拉出值时必须处理它们。
var results = (from l in MyTable
group l by new {l.Name, l.Service} into grp
where (grp.Key.Name == "dcc" && grp.Key.Service == "Main")
|| (grp.Key.Name == "dcc" && grp.Key.Service == "DB")
|| ....
select new
{
grp.Key,
Value = grp.OrderByDescending(x => x.Time).Select(x => x.Value).First()
}).ToDictionary(x => x.Key, x => x.Value);
然后拿出结果
results.TryGetValue(new { Name = "dcc", Service = "Main" }, out var cSRez);
我不确定这是否会作为单个查询执行得更快,因为查询有点复杂,所以我认为它可能取决于服务器端查询时间与查询设置和数据传输时间。
您可以转换为一次查询,一次收集所有答案,然后为每个变量打破该答案。
第一个答案获取结果并转换为值的双嵌套Dictionary
,然后将变量拉出Dictionary
:
var ansd = (from l in MyTable
where new[] { "dcc", "ddc", "mc" }.Contains(l.Name) && new[] { "Main", "DB" }.Contains(l.Service)
group l by new { l.Name, l.Service } into ag
select new {
ag.Key.Name,
ag.Key.Service,
Value = ag.OrderByDescending(l => l.Time).First().Value
})
.GroupBy(nsv => nsv.Name)
.ToDictionary(nsvg => nsvg.Key, nsvg => nsvg.ToDictionary(nsv => nsv.Service, arv => arv.Value));
long? cSRez = null, cDRez = null, dSRez = null, dDRez = null, mSRez = null, mDRez = null;
if (ansd.TryGetValue("dcc", out var td)) td.TryGetValue("Main", out cSRez);
if (ansd.TryGetValue("dcc", out td)) td.TryGetValue("DB", out cDRez);
if (ansd.TryGetValue("ddc", out td)) td.TryGetValue("Main", out dSRez);
if (ansd.TryGetValue("ddc", out td)) td.TryGetValue("DB", out dDRez);
if (ansd.TryGetValue("mc", out td)) td.TryGetValue("Main", out mSRez);
if (ansd.TryGetValue("mc", out td)) td.TryGetValue("DB", out mDRez);
鉴于只有六个答案,为他们创建Dictionary
s可能有点矫枉过正。相反,您可以(按顺序)找到匹配的答案:
var ansl = (from l in MyTable
where new[] { "dcc", "ddc", "mc" }.Contains(l.Name) && new[] { "Main", "DB" }.Contains(l.Service)
group l by new { l.Name, l.Service } into ag
select new {
ag.Key.Name,
ag.Key.Service,
Value = ag.OrderByDescending(l => l.Time).First().Value
})
.ToList();
var cSRez = ansl.FirstOrDefault(ansv => ansv.Name == "dcc" && ansv.Service == "Main");
var cDRez = ansl.FirstOrDefault(ansv => ansv.Name == "dcc" && ansv.Service == "DB");
var dSRez = ansl.FirstOrDefault(ansv => ansv.Name == "ddc" && ansv.Service == "Main");
var dDRez = ansl.FirstOrDefault(ansv => ansv.Name == "ddc" && ansv.Service == "DB");
var mSRez = ansl.FirstOrDefault(ansv => ansv.Name == "mc" && ansv.Service == "Main");
var mDRez = ansl.FirstOrDefault(ansv => ansv.Name == "mc" && ansv.Service == "DB");
只需将查询放在一个私有方法中,该方法接受一个Expression并返回Value,并为每个变量(例如cDRez,cRuz等)调用它,只需传递不同的表达式即可。
private Value GetValue(Expression<Func<MyTable, bool>> filter) {
return MyTable.Where(filter).OrderByDescending(o => o.Time).Select(s => s.Value).FirstOrDefault();
}
将其称为每个变量的不同过滤器:
var cSRez = GetValue(l => l.Name == "dcc" && l.Service == "Main");
var cDRez = GetValue(l => l.Name == "dcc" && l.Service == "DB");
var dSRez = GetValue(l => l.Name == "ddc" && l.Service == "Main");
var dDRez = GetValue(l => l.Name == "ddc" && l.Service == "DB");
var mSRez = GetValue(l => l.Name == "mc" && l.Service == "Main");
var mDRez = GetValue(l => l.Name == "mc" && l.Service == "DB");
我不确定EF如何将此查询转换为SQL,但我会尝试这种方法:
var rawData = MyTable
.Where(l => (l.Name=="dcc" || l.Name=="ddc" || l.Name=="mc") && (l.Service=="Main" || l.Service=="Db"))
.GroupBy(l => new { l.Name, l.Service })
.Select(g => g.OrderByDescending(l => l.Time).First())
.ToList();
这应该最多可以为您的程序产生六行。现在,您可以通过指定Name
和Service
的特定组合来检索每一行:
var cSRez = rawData.FirstOrDefault(l => l.Name == "dcc" && l.Service == "Main");
var cDRez = rawData.FirstOrDefault(l => l.Name == "dcc" && l.Service == "DB");
var dSRez = rawData.FirstOrDefault(l => l.Name == "ddc" && l.Service == "Main");
var dDRez = rawData.FirstOrDefault(l => l.Name == "ddc" && l.Service == "DB");
var mSRez = rawData.FirstOrDefault(l => l.Name == "mc" && l.Service == "Main");
var mDRez = rawData.FirstOrDefault(l => l.Name == "mc" && l.Service == "DB");
请注意,rawData
上的六个查询在内存中以最多六个固定大小的列表执行,因此它们不会花费额外的RDBMS往返次数。