我是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),但我认为这不是最好的主意。
是否可以将这六个独立的查询折叠成一个查询?
答案 0 :(得分:4)
您需要对“名称和服务”进行分组,然后对所需的特定对进行过滤,然后从“首次匹配”中选择“名称和服务”以及“值”。请注意,结果中将不显示任何不存在的对,并且在将值拉出时必须处理该对。
undefined
然后提取结果
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);
答案 1 :(得分:1)
由于查询有些复杂,因此我不确定这样做是否会更快地作为单个查询执行,所以我认为这可能取决于服务器端查询时间与查询设置和数据传输时间。
您可以转换为一个查询,一次收集所有答案,然后为每个变量分解答案。
第一个答案获取结果,并将其转换为值的双嵌套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
可能会过头了。相反,您可以(依次)找到匹配的答案:
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");
答案 2 :(得分:0)
我不确定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的费用。
答案 3 :(得分:0)
只需将您的查询放入一个私有方法中即可,该私有方法接受Exression并返回Value,并为每个变量(例如cDRez,cSRez等)简单地传递不同的表达式即可对其进行调用。
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");