简体   繁体   English

C#Linq单个查询中的多个查询

[英]C# Linq multiple queries in a single one

I'm new in Linq and I'm trying to optimize some queries and I don't have any idea if it is possible for this queries: 我是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();

to become a single one. 成为一个。 I was thinking about row_number() over(partition by... (SQL) but I don't think this is the best idea for doing this. 我在考虑row_number() over(partition by... (SQL),但我不认为这是执行此操作的最佳方法。

It is possible to collapse this six separate queries into a single one? 是否可以将这六个独立的查询合并为一个查询?

You'd need to group on the Name and Service and then filter on the specific pairs you want then select the Name and Service and the Value from the First match. 您需要对“名称和服务”进行分组,然后根据需要对特定对进行过滤,然后从“第一个匹配项”中选择“名称和服务”以及“值”。 Note that any pairs that do not exist will not be represented in the results and you'd have to handle that when you pull the values out. 请注意,任何不存在的对都不会在结果中表示,并且在将值拉出时必须处理该对。

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

Then to pull out the results 然后拿出结果

results.TryGetValue(new { Name = "dcc", Service = "Main" }, out var cSRez);

I am not sure if this will execute faster as a single query or not, as the query is somewhat complex, so I think it may depend on server side query time versus query setup and data transmission time. 我不确定此查询作为单个查询是否会执行得更快,因为查询有些复杂,因此我认为这可能取决于服务器端查询时间与查询设置和数据传输时间。

You can convert into a single query that gathers all the answers at once, and then break that answer up for each variable. 您可以转换为单个查询,一次收集所有答案,然后为每个变量分解答案。

This first answer takes the result and converts into a double nested Dictionary for the values and then pulls the variables out of the Dictionary : 第一个答案将结果转换为值的双嵌套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);

Given that there are only six answers, creating Dictionary s for them may be overkill. 鉴于只有六个答案,因此为它们创建Dictionary可能是过大的。 Instead, you can just (sequentially) find the matching answers: 相反,您可以(依次)找到匹配的答案:

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

I am not sure how EF would translate this query to SQL, but I would try this approach: 我不确定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();

This should yield up to six rows of interest to your program. 这将产生多达六行的程序兴趣。 Now you can retrieve each row by specifying the specific combination of Name and Service : 现在,您可以通过指定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");

Note that the six queries on rawData are performed in memory on a list of fixed size of up to six items, so they are not costing you additional round-trips to RDBMS. 请注意,对rawData的六个查询是在内存中执行的,该查询最多包含六个固定大小的列表,因此它们不会花费额外的RDBMS往返费用。

Just put your query in a private method that takes an Exression and returns Value and call it for each variable (eg cDRez, cSRez etc) simply passing different expressions. 只需将您的查询放入一个私有方法中,该方法接受一个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();
}

Call it like with different filters for each variable: 像对每个变量使用不同的过滤器一样调用它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM