我目前有以下代码来生成过去30天的销售报告。我想知道是否可以使用linq一步生成此报告,而不是我在这里的相当基本的循环。
根据我的要求,每天都需要向我返还一个值,所以如果任何一天没有销售,则返回0。
那里的任何Sum linq示例都没有解释如何包含where过滤器,所以我很困惑如何获得每天的总金额,或者如果没有销售则为0,最后几天我通过了。
感谢您的帮助, 富
//setup date ranges to use
DateTime startDate = DateTime.Now.AddDays(-29);
DateTime endDate = DateTime.Now.AddDays(1);
TimeSpan startTS = new TimeSpan(0, 0, 0);
TimeSpan endTS = new TimeSpan(23, 59, 59);
using (var dc = new DataContext())
{
//get database sales from 29 days ago at midnight to the end of today
var salesForDay = dc.Orders.Where(b => b.OrderDateTime > Convert.ToDateTime(startDate.Date + startTS) && b.OrderDateTime <= Convert.ToDateTime(endDate.Date + endTS));
//loop through each day and sum up the total orders, if none then set to 0
while (startDate != endDate)
{
decimal totalSales = 0m;
DateTime startDay = startDate.Date + startTS;
DateTime endDay = startDate.Date + endTS;
foreach (var sale in salesForDay.Where(b => b.OrderDateTime > startDay && b.OrderDateTime <= endDay))
{
totalSales += (decimal)sale.OrderPrice;
}
Response.Write("From Date: " + startDay + " - To Date: " + endDay + ". Sales: " + String.Format("{0:0.00}", totalSales) + "<br>");
//move to next day
startDate = startDate.AddDays(1);
}
}
编辑: 约翰内斯的答案是处理我的查询的好方法。下面是对代码的调整,以便在其他人遇到此问题时使其适用于此示例。这将从allDays表执行外部联接,并在当天没有销售时返回0值。
var query = from d in allDays
join s in salesByDay on d equals s.Day into j
from s in j.DefaultIfEmpty()
select new { Day = d, totalSales = (s != null) ? s.totalSales : 0m };
答案 0 :(得分:4)
您可以按日对所有数据进行分组,并对这些组进行总结。为了满足每天总和的要求,即使是那些没有订单的要求,您也可以加入所有日期的列表,或者只是使用循环来确保包含所有日期。小提示:如果您按DateTime.Date
属性进行比较,则无需明确设置时间。
以下是使用生成器函数的解决方案(取自MoreLinq项目):
public static partial class MoreEnumerable
{
public static IEnumerable<TResult> GenerateByIndex<TResult>(Func<int, TResult> generator)
{
// Looping over 0...int.MaxValue inclusive is a pain. Simplest is to go exclusive,
// then go again for int.MaxValue.
for (int i = 0; i < int.MaxValue; i++)
{
yield return generator(i);
}
yield return generator(int.MaxValue);
}
}
public class MyClass
{
private void test()
{
DateTime startDate = DateTime.Now.AddDays(-29);
DateTime endDate = DateTime.Now.AddDays(1);
using (var dc = new DataContext())
{
//get database sales from 29 days ago at midnight to the end of today
var salesForPeriod = dc.Orders.Where(b => b.OrderDateTime > startDate.Date && b.OrderDateTime <= endDate.Date);
var allDays = MoreEnumerable.GenerateByIndex(i => startDate.AddDays(i)).Take(30);
var salesByDay = from s in salesForPeriod
group s by s.OrderDateTime.Date into g
select new {Day = g.Key, totalSales = g.Sum(x=>(decimal)x.OrderPrice};
var query = from d in allDays
join s in salesByDay on s.Day equals d
select new {Day = s.Day , totalSales = (s != null) ? s.totalSales : 0m;
foreach (var item in query)
{
Response.Write("Date: " +item.Day.ToString() " Sales: " + String.Format("{0:0.00}", item.totalSales) + "<br>");
}
}
}
}
答案 1 :(得分:0)
我认为如果您的枚举不包含一天的数据,则无法返回当天的值。我能想到的最好的方法是创建一个Order对象列表,每天的值为零,并创建一个带有查询结果的联合。这就是我想出的。但是,我认为循环遍历每个组,检查是否“跳过”任何一天,并且为每天“跳过”返回零比在内存中创建自己的枚举更简单(除非您希望枚举“缺少缺口” “ 填写)。请注意,我基本上假设对于每个组,您想要将一天中的所有值相加。
List<Order> zeroList = new List<Order>();
while (startDate <= endDate)
{
zeroList.Add(new Order { OrderDateTime = startDate, OrderPrice = 0 });
startDate = startDate.AddDays(1)
}
var comboList = zeroList.Union(dc.Orders.Where(b => b.OrderDateTime > Convert.ToDateTime(startDate.Date + startTS) && b.OrderDateTime <= Convert.ToDateTime(endDate.Date + endTS))
var groupedTotalSales = comboList.GroupBy(b => b.OrderDateTime.Date)
.Select(b => new { StartDate = Convert.ToDateTime(b.Key + startTS), EndDate = Convert.ToDateTime(b.Key + endTS), Sum = b.Sum(x => x.OrderPrice });
foreach (totalSale in groupedTotalSales)
Response.Write("From Date: " + totalSale.StartDate + " - To Date: " + totalSale.EndDate + ". Sales: " + String.Format("{0:0.00}", (decimal)totalSale.Sum) + "<br/>");