通过语句加速 linq 组
Speed up the linq group by statement
我有一个table这样的
UserID Year EffectiveDate Type SpecialExpiryDate
1 2015 7/1/2014 A
1 2016 7/1/2015 B 10/1/2015
table中没有ExpriyDate
,因为有效期只有一年,所以有效期可以从生效日期算起加一年
我要得到的结果是这样的(当年生效日期和下一年到期日期)
UserID EffectiveDate ExpiryDate
1 7/1/2014 7/1/2016
而如果用户的类型是B,那么会有一个特殊的到期日期,所以对于这个人,结果将是
UserID EffectiveDate ExpiryDate
1 7/1/2014 10/1/2015
这是我写的代码
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)
}
);
代码可以得到我需要的结果,但问题是结果集中大约有10000条记录,耗时5到6秒。该项目用于网络搜索API,所以我想加快速度,有没有更好的查询方式?
编辑
抱歉我弄错了,在select子句中应该是
EffectiveDate = t.LastOrDefault().EffectiveDate
但是在C#的Linq中,不支持将这个LastOrDefault函数转给sql,又引发了新的问题,请问最简单的获取组中第二项的方法是什么?
试试这个:
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);
.SelectMany(y => y.Take(1)
有效地完成了代码的 .FirstOrDefault()
部分。通过一次而不是对许多属性执行此操作,您可以极大地提高速度。
在我使用类似结构的查询执行的测试中,当使用您的方法时,我得到这些子查询 运行:
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]
(每个记录号继续进行两个子查询。)
如果我运行我的方法我得到了这个单一的查询:
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
我的方法应该快得多。
您可以使用数据库中的 View
即时生成计算数据。
类似这样的东西(伪代码):
Create View vwUsers AS
Select
UserID,
Year,
EffectiveDate,
EffectiveData + 1 as ExpiryDate, // <--
Type,
SpecialExpiryDate
From
tblUsers
然后将您的 LINQ
查询连接到那个。
我有一个table这样的
UserID Year EffectiveDate Type SpecialExpiryDate
1 2015 7/1/2014 A
1 2016 7/1/2015 B 10/1/2015
table中没有ExpriyDate
,因为有效期只有一年,所以有效期可以从生效日期算起加一年
我要得到的结果是这样的(当年生效日期和下一年到期日期)
UserID EffectiveDate ExpiryDate
1 7/1/2014 7/1/2016
而如果用户的类型是B,那么会有一个特殊的到期日期,所以对于这个人,结果将是
UserID EffectiveDate ExpiryDate
1 7/1/2014 10/1/2015
这是我写的代码
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)
}
);
代码可以得到我需要的结果,但问题是结果集中大约有10000条记录,耗时5到6秒。该项目用于网络搜索API,所以我想加快速度,有没有更好的查询方式?
编辑
抱歉我弄错了,在select子句中应该是
EffectiveDate = t.LastOrDefault().EffectiveDate
但是在C#的Linq中,不支持将这个LastOrDefault函数转给sql,又引发了新的问题,请问最简单的获取组中第二项的方法是什么?
试试这个:
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);
.SelectMany(y => y.Take(1)
有效地完成了代码的 .FirstOrDefault()
部分。通过一次而不是对许多属性执行此操作,您可以极大地提高速度。
在我使用类似结构的查询执行的测试中,当使用您的方法时,我得到这些子查询 运行:
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]
(每个记录号继续进行两个子查询。)
如果我运行我的方法我得到了这个单一的查询:
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
我的方法应该快得多。
您可以使用数据库中的 View
即时生成计算数据。
类似这样的东西(伪代码):
Create View vwUsers AS
Select
UserID,
Year,
EffectiveDate,
EffectiveData + 1 as ExpiryDate, // <--
Type,
SpecialExpiryDate
From
tblUsers
然后将您的 LINQ
查询连接到那个。