简体   繁体   English

按语句加速linq组

[英]Speed up the linq group by statement

I have a table like this 我有一张这样的桌子

UserID   Year   EffectiveDate   Type    SpecialExpiryDate
     1   2015   7/1/2014        A   
     1   2016   7/1/2015        B       10/1/2015

there is no ExpriyDate in the table because it is only valid for one year, so the expiry date can be calculated from the effective date by adding a year. 表中没有ExpriyDate ,因为它仅在一年内有效,因此可以通过添加一年从生效日期计算到期日。

The result I want to get is like this (the current year's effective date and the next year's expiry date) 我想得到的结果是这样的(当年的生效日期和下一年的到期日)

UserID   EffectiveDate   ExpiryDate
     1    7/1/2014        7/1/2016

And If the user's type is B, then there will be a special expiry date, so for this person, the result will be 如果用户的类型是B,那么将有一个特殊的到期日期,因此对于此人,结果将是

UserID   EffectiveDate   ExpiryDate
     1    7/1/2014        10/1/2015

Here is the code I wrote 这是我写的代码

var result = db.Table1
            .Where(x => x.Year>= 2015 && (x.Type == "A" || x.Type == "B"))
            .GroupBy(y => y.UserID)
            .OrderByDescending(x => x.FirstOrDefault().Year)
            .Select(t => new
                         {
                             ID = t.Key,
                             Type = t.FirstOrDefault().Type,
                             EffectiveDate = t.FirstOrDefault().EffectiveDate,
                             ExpiryDate = t.FirstOrDefault().SpecialExpiryDate != null ? t.FirstOrDefault().SpecialExpiryDate : (t.Count() >= 2 ? NextExpiryDate : CurrentExpiryDate)
                          }
                    );

The code can get the result I need, but the problem is that in the result set there are about 10000 records which took about 5 to 6 seconds. 代码可以得到我需要的结果,但问题是在结果集中有大约10000条记录需要大约5到6秒。 The project is for a web search API, so I want to speed it up, is there a better way to do the query? 该项目是针对网络搜索API的,所以我想加快速度,是否有更好的方法来进行查询?

Edit 编辑

Sorry I made a mistake, in the select clause it should be 对不起,我犯了一个错误,应该在select子句中

EffectiveDate = t.LastOrDefault().EffectiveDate

but in the Linq of C#, it didn't support this LastOrDefault function transfered to sql, and it cause the new problem, what is the easiest way to get the second item of the group? 但在C#的Linq中,它不支持将此LastOrDefault函数转移到sql,并且它会导致新问题,获取组中第二项的最简单方法是什么?

Try this: 尝试这个:

var result =
    db
        .Table1
        .Where(x => x.Year>= 2015 && (x.Type == "A" || x.Type == "B"))
        .GroupBy(y => y.UserID)
        .SelectMany(y => y.Take(1), (y, z) => new
        {
            ID = y.Key,
            z.Type,
            z.EffectiveDate,
            ExpiryDate = z.SpecialExpiryDate != null
                ? z.SpecialExpiryDate 
                : (t.Count() >= 2 ? NextExpiryDate : CurrentExpiryDate),
            z.Year,
        })
        .OrderByDescending(x => x.Year);

The .SelectMany(y => y.Take(1) effectively does the .FirstOrDefault() part of your code. By doing this once rather than for many properties you may improve the speed immensely. .SelectMany(y => y.Take(1)有效地执行代码的.FirstOrDefault()部分。通过执行此操作而不是许多属性,您可以极大地提高速度。

In a test I performed using a similarly structured query I got these sub-queries being run when using your approach: 在我使用类似结构化查询执行的测试中,我在使用您的方法时运行了这些子查询:

SELECT t0.increment_id
FROM sales_flat_order AS t0
GROUP BY t0.increment_id

SELECT t0.hidden_tax_amount
FROM sales_flat_order AS t0
WHERE ((t0.increment_id IS NULL AND @n0 IS NULL) OR (t0.increment_id = @n0))
LIMIT 0, 1
-- n0 = [100000001]

SELECT t0.customer_email
FROM sales_flat_order AS t0
WHERE ((t0.increment_id IS NULL AND @n0 IS NULL) OR (t0.increment_id = @n0))
LIMIT 0, 1
-- n0 = [100000001]

SELECT t0.hidden_tax_amount
FROM sales_flat_order AS t0
WHERE ((t0.increment_id IS NULL AND @n0 IS NULL) OR (t0.increment_id = @n0))
LIMIT 0, 1
-- n0 = [100000002]

SELECT t0.customer_email
FROM sales_flat_order AS t0
WHERE ((t0.increment_id IS NULL AND @n0 IS NULL) OR (t0.increment_id = @n0))
LIMIT 0, 1
-- n0 = [100000002]

(This continued on for two sub-queries per record number.) (每个记录号继续进行两次子查询。)

If I ran my approach I got this single query: 如果我运行我的方法,我得到了这个单一的查询:

SELECT t0.increment_id, t1.hidden_tax_amount, t1.customer_email
FROM (
  SELECT t2.increment_id
  FROM sales_flat_order AS t2
  GROUP BY t2.increment_id
  ) AS t0
CROSS APPLY (
  SELECT t3.customer_email, t3.hidden_tax_amount
  FROM sales_flat_order AS t3
  WHERE ((t3.increment_id IS NULL AND t0.increment_id IS NULL) OR (t3.increment_id = t0.increment_id))
  LIMIT 0, 1
  ) AS t1

My approach should be much faster. 我的方法应该快得多。

You could generate the calculated data on the fly, using a View in your database. 您可以使用数据库中的View生成计算数据。

Something like this (pseudocode): 像这样的东西(伪代码):

Create View vwUsers AS 
    Select 
        UserID, 
        Year, 
        EffectiveDate, 
        EffectiveData + 1 as ExpiryDate,   // <-- 
        Type, 
        SpecialExpiryDate
    From 
        tblUsers

And just connect your LINQ query to that. 只需将LINQ查询连接到该查询即可。

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

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