LINQ中的条件组合和
我可以通过linq访问以下实体:LINQ中的条件组合和,linq,linq-to-sql,linq-to-entities,Linq,Linq To Sql,Linq To Entities,我可以通过linq访问以下实体: 一天 数量 类型(1或2) 我在同一天和同一类型有许多行,我需要按月汇总它们的金额。这很简单: from r in rows group r by new { r.Date.Year, r.Date.Month } into g select new { Date = new DateTime(g.Key.Year, g.Key.Month, 1), Hours = g.Sum(a =&
- 一天
- 数量
- 类型(1或2)
from r in rows
group r by new { r.Date.Year, r.Date.Month }
into g
select
new
{
Date = new DateTime(g.Key.Year, g.Key.Month, 1),
Hours = g.Sum(a => a.Amount)
};
但是,我有一个特殊规则需要在同一LINQ中实现,我希望得到一些帮助:
如果某一天有任何类型2,则该天应仅为类型2之和。否则,应在类型1上进行总结
请注意,类型1和类型2之间的区别是每天,总和是每月
更新
由于我要处理大量数据,我需要在一个数据库调用中获取所有数据,我无法将其加载到内存中并在内存中进行管理。这对您来说会是什么样子
var query =
from r in rows.ToArray()
group r by new { r.Date.Year, r.Date.Month } into g
let lookup = g.ToLookup(x => x.Type, x => x.Amount)
let Hours = lookup[2].Any() ? lookup[2].Sum() : lookup[1].Sum()
select new
{
Date = new DateTime(g.Key.Year, g.Key.Month, 1),
Hours,
};
请注意.ToArray()
,因为您需要将数据放入内存中才能执行此操作
我假设Type
是一个值为1
或2
的整数
进一步考虑,我认为不可能在一个查询中进行这种分组而不将其放入内存 因此,最好的选择是最小化内存。如果这不起作用,那么您需要将其分解为几个查询
var query1 =
from r in rows
group r.Amount by new
{
r.Date.Year,
r.Date.Month,
r.Type,
} into g
select new
{
g.Key.Year,
g.Key.Month,
g.Key.Type,
Amount = g.Sum(),
};
var query2 =
from r in query1.ToArray()
group r by new
{
r.Year,
r.Month,
} into g
let lookup = g.ToLookup(x => x.Type, x => x.Amount)
let Hours = lookup[2].Any() ? lookup[2].Sum() : lookup[1].Sum()
select new
{
Date = new DateTime(g.Key.Year, g.Key.Month, 1),
Hours,
};
这是一种可能的选择吗?好的,我想我已经解决了这个问题。我已经用一些数据对它进行了测试,这是有效的 Testdata和SQL testquery:
DECLARE @table TABLE (
Datum DATETIME,
Amount INT,
[Type] INT
)
INSERT INTO @table (Datum, Amount, [Type]) values
('2012-01-01',200,1),
('2012-01-01',100,2),
('2012-01-02',500,1),
('2012-03-01',200,1),
('2012-03-01',100,1),
('2012-03-02',500,2)
SELECT MONTH(Datum), YEAR(Datum), COUNT(*), SUM(Amount)
FROM @table t
INNER JOIN (
SELECT DAY (Datum) AS _day, MONTH(Datum) AS _month, YEAR(Datum) _year,
MAX([Type]) as _type
FROM @table
GROUP BY DAY (Datum), MONTH(Datum), YEAR(Datum)
) X
ON _month = MONTH (T.Datum)
AND _year = YEAR(T.Datum)
AND _day = DAY(T.Datum)
AND _type = T.[Type]
GROUP BY MONTH(Datum), YEAR(Datum)
结果:
(No column name) (No column name) (No column name) (No column name)
1 2012 2 600
3 2012 3 800
翻译后的LINQ查询,使用L2S和带有测试数据的“真实”测试表进行测试
using (DataClasses1DataContext ctx = new DataClasses1DataContext()) {
var rows = ctx.Tests;
var query = rows
.Join(
rows.GroupBy(rr =>
new { rr.Datum.Day, rr.Datum.Month, rr.Datum.Year },
(key, data) => new { Year = key.Year, Month = key.Month, Day = key.Day, MaxType = data.Select(xxx => xxx.Type).Max() }
),
rr => new { Day = rr.Datum.Day, Month = rr.Datum.Month, Year = rr.Datum.Year, Type = rr.Type },
rr => new { Day = rr.Day, Month = rr.Month, Year = rr.Year, Type = rr.MaxType },
(r, r1) => r
)
.GroupBy(r =>
new { Year = r.Datum.Year, Month = r.Datum.Month },
(key, data) => new { Year = key.Year, Month = key.Month, Amount = data.Select(xx => xx.Amount).Sum() }
)
.ToList();
}
这将返回相同的结果
有趣的是,L2S从Linq查询生成的SQL查询
SELECT [t7].[value] AS [Year], [t7].[value2] AS [Month], (
SELECT SUM([t8].[Amount])
FROM [dbo].[Test] AS [t8]
INNER JOIN (
SELECT [t11].[value3], [t11].[value2], [t11].[value], (
SELECT MAX([t12].[Type])
FROM [dbo].[Test] AS [t12]
WHERE ((([t11].[value] IS NULL) AND (DATEPART(Day, [t12].[Datum]) IS NULL)) OR (([t11].[value] IS NOT NULL) AND (DATEPART(Day, [t12].[Datum]) IS NOT NULL) AND ((([t11].[value] IS NULL) AND (DATEPART(Day, [t12].[Datum]) IS NULL)) OR (([t11].[value] IS NOT NULL) AND (DATEPART(Day, [t12].[Datum]) IS NOT NULL) AND ([t11].[value] = DATEPART(Day, [t12].[Datum])))))) AND ((([t11].[value2] IS NULL) AND (DATEPART(Month, [t12].[Datum]) IS NULL)) OR (([t11].[value2] IS NOT NULL) AND (DATEPART(Month, [t12].[Datum]) IS NOT NULL) AND ((([t11].[value2] IS NULL) AND (DATEPART(Month, [t12].[Datum]) IS NULL)) OR (([t11].[value2] IS NOT NULL) AND (DATEPART(Month, [t12].[Datum]) IS NOT NULL) AND ([t11].[value2] = DATEPART(Month, [t12].[Datum])))))) AND ((([t11].[value3] IS NULL) AND (DATEPART(Year, [t12].[Datum]) IS NULL)) OR (([t11].[value3] IS NOT NULL) AND (DATEPART(Year, [t12].[Datum]) IS NOT NULL) AND ((([t11].[value3] IS NULL) AND (DATEPART(Year, [t12].[Datum]) IS NULL)) OR (([t11].[value3] IS NOT NULL) AND (DATEPART(Year, [t12].[Datum]) IS NOT NULL) AND ([t11].[value3] = DATEPART(Year, [t12].[Datum]))))))
) AS [value4]
FROM (
SELECT [t10].[value], [t10].[value2], [t10].[value3]
FROM (
SELECT DATEPART(Day, [t9].[Datum]) AS [value], DATEPART(Month, [t9].[Datum]) AS [value2], DATEPART(Year, [t9].[Datum]) AS [value3]
FROM [dbo].[Test] AS [t9]
) AS [t10]
GROUP BY [t10].[value], [t10].[value2], [t10].[value3]
) AS [t11]
) AS [t13] ON (DATEPART(Day, [t8].[Datum]) = [t13].[value]) AND (DATEPART(Month, [t8].[Datum]) = [t13].[value2]) AND (DATEPART(Year, [t8].[Datum]) = [t13].[value3]) AND ([t8].[Type] = [t13].[value4])
WHERE ((([t7].[value] IS NULL) AND (DATEPART(Year, [t8].[Datum]) IS NULL)) OR (([t7].[value] IS NOT NULL) AND (DATEPART(Year, [t8].[Datum]) IS NOT NULL) AND ((([t7].[value] IS NULL) AND (DATEPART(Year, [t8].[Datum]) IS NULL)) OR (([t7].[value] IS NOT NULL) AND (DATEPART(Year, [t8].[Datum]) IS NOT NULL) AND ([t7].[value] = DATEPART(Year, [t8].[Datum])))))) AND ((([t7].[value2] IS NULL) AND (DATEPART(Month, [t8].[Datum]) IS NULL)) OR (([t7].[value2] IS NOT NULL) AND (DATEPART(Month, [t8].[Datum]) IS NOT NULL) AND ((([t7].[value2] IS NULL) AND (DATEPART(Month, [t8].[Datum]) IS NULL)) OR (([t7].[value2] IS NOT NULL) AND (DATEPART(Month, [t8].[Datum]) IS NOT NULL) AND ([t7].[value2] = DATEPART(Month, [t8].[Datum]))))))
) AS [Amount]
FROM (
SELECT [t6].[value], [t6].[value2]
FROM (
SELECT DATEPART(Year, [t0].[Datum]) AS [value], DATEPART(Month, [t0].[Datum]) AS [value2]
FROM [dbo].[Test] AS [t0]
INNER JOIN (
SELECT [t3].[value3], [t3].[value2], [t3].[value], (
SELECT MAX([t4].[Type])
FROM [dbo].[Test] AS [t4]
WHERE ((([t3].[value] IS NULL) AND (DATEPART(Day, [t4].[Datum]) IS NULL)) OR (([t3].[value] IS NOT NULL) AND (DATEPART(Day, [t4].[Datum]) IS NOT NULL) AND ((([t3].[value] IS NULL) AND (DATEPART(Day, [t4].[Datum]) IS NULL)) OR (([t3].[value] IS NOT NULL) AND (DATEPART(Day, [t4].[Datum]) IS NOT NULL) AND ([t3].[value] = DATEPART(Day, [t4].[Datum])))))) AND ((([t3].[value2] IS NULL) AND (DATEPART(Month, [t4].[Datum]) IS NULL)) OR (([t3].[value2] IS NOT NULL) AND (DATEPART(Month, [t4].[Datum]) IS NOT NULL) AND ((([t3].[value2] IS NULL) AND (DATEPART(Month, [t4].[Datum]) IS NULL)) OR (([t3].[value2] IS NOT NULL) AND (DATEPART(Month, [t4].[Datum]) IS NOT NULL) AND ([t3].[value2] = DATEPART(Month, [t4].[Datum])))))) AND ((([t3].[value3] IS NULL) AND (DATEPART(Year, [t4].[Datum]) IS NULL)) OR (([t3].[value3] IS NOT NULL) AND (DATEPART(Year, [t4].[Datum]) IS NOT NULL) AND ((([t3].[value3] IS NULL) AND (DATEPART(Year, [t4].[Datum]) IS NULL)) OR (([t3].[value3] IS NOT NULL) AND (DATEPART(Year, [t4].[Datum]) IS NOT NULL) AND ([t3].[value3] = DATEPART(Year, [t4].[Datum]))))))
) AS [value4]
FROM (
SELECT [t2].[value], [t2].[value2], [t2].[value3]
FROM (
SELECT DATEPART(Day, [t1].[Datum]) AS [value], DATEPART(Month, [t1].[Datum]) AS [value2], DATEPART(Year, [t1].[Datum]) AS [value3]
FROM [dbo].[Test] AS [t1]
) AS [t2]
GROUP BY [t2].[value], [t2].[value2], [t2].[value3]
) AS [t3]
) AS [t5] ON (DATEPART(Day, [t0].[Datum]) = [t5].[value]) AND (DATEPART(Month, [t0].[Datum]) = [t5].[value2]) AND (DATEPART(Year, [t0].[Datum]) = [t5].[value3]) AND ([t0].[Type] = [t5].[value4])
) AS [t6]
GROUP BY [t6].[value], [t6].[value2]
) AS [t7]
我不知道这个SQL有多高效,你得试试
rows
.GroupBy(
r => new { r.Date.Year, r.Date.Month, r.Date.Day, r.Type },
(r, rr) => new { r.Year, r.Month, r.Day, r.Type, Amount = rr.Sum(rrr => rrr.Amount) })
.GroupBy(
r => new { r.Year, r.Month, r.Day },
(r, rr) => new { r.Year, r.Month, r.Day, Amount = rr.OrderByDescending(rrr => rrr.Type).Select(rrr => rrr.Amount).First() })
.GroupBy(
r => new { r.Year, r.Month },
(r, rr) => new { r.Year, r.Month, Amount = rr.Sum(rrr => rrr.Amount) })
这一点背后的比率非常简单:“如果至少有一个类型2记录,则只能求和类型2记录”的要求可以通过简单地按类型分组记录(当然是在几天内)来实现。它为什么有效?因为我们将所有记录分为两组,类型2(如果至少有一个类型2记录,则应使用该类型)和类型1(实际上表示“没有类型2时的所有记录”)。第二部分(选择总和)更简单:我们只需按降序类型(即类型2的总和,类型1的总和)对组进行排序(在一天内),然后取第一部分,如果存在,则为类型2,否则为类型1
坦率地说,这是一种人人都不喜欢的“智能代码”,因为没有人能从一眼就能理解它是如何工作的。这个怎么样
from r in rows
group r by new { r.Date.Year, r.Date.Month }
into g
let type2Days = g.Where( a => a.Type == 2 ).Select( a => a.Date.Day ).Distinct()
let filtered = g.Where( a => a.Type == 2 || type2Days.Contains(a.Date.Day) == false )
select
new
{
Date = new DateTime(g.Key.Year, g.Key.Month, 1),
Hours = filtered.Sum(a => a.Amount)
};
谢谢你的回答,但不幸的是我无法将数据加载到内存中。我已经更新了答案。很抱歉一开始没有具体说明。太多了,我不想把它放在内存中。我已经进一步考虑并修改了我的答案。这是一个折衷方案,但可能对你有用。谢谢你的努力。这不是每月而不是每天的区别吗?在第一个查询中,我认为您丢失了有关各个日期类型的信息。
query1
捕获了第一个分组中的Type
值,因此它不应该丢失。您可以在数据集上尝试一下吗?谢谢。坦白地说,我没做db检查就成功了。塞格,成功了!做得很好。现在我将试着理解实际发生的事情:)我将在我的答案后面加上比率。你有一个观点,这是不容易理解的。我在考虑把它改写成漂亮的LINQ格式,看看它是否更容易阅读……Maarten,谢谢你给了我一个非常好的、彻底的答案,我可能很容易使用。我选择先试试塞格的答案,因为林克更漂亮:)足够公平!这是更好的答案,做得很好。它很有逻辑性和可读性。但是,您首先分组到g中,然后使用g进行过滤。这样行吗?我们每天都需要这个型号。@Niels它应该能用。对于每个月,它将获得类型为2的行或在没有任何类型2行的日期的行。如果需要,可以在分组之前进行筛选。您只需使用Date.Date
而不是Date.Day
。