C#Linq在一个查询中有多个查询

问题描述 投票:1回答:4

我是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),但我认为这不是最好的想法。

可以将这六个单独的查询折叠成一个查询吗?

c# sql entity-framework linq
4个回答
4
投票

您需要对名称和服务进行分组,然后对所需的特定对进行过滤,然后从第一个匹配项中选择名称和服务以及值。请注意,任何不存在的对都不会在结果中表示,并且在拉出值时必须处理它们。

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);

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);

鉴于只有六个答案,为他们创建Dictionarys可能有点矫枉过正。相反,您可以(按顺序)找到匹配的答案:

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");

0
投票

只需将查询放在一个私有方法中,该方法接受一个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");

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();

这应该最多可以为您的程序产生六行。现在,您可以通过指定NameService的特定组合来检索每一行:

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往返次数。

© www.soinside.com 2019 - 2024. All rights reserved.