C# 使用Linq to SQL生成销售报告

C# 使用Linq to SQL生成销售报告,c#,linq-to-sql,grouping,sum,C#,Linq To Sql,Grouping,Sum,我目前有以下代码来生成过去30天的销售报告。我想知道是否有可能使用linq一步生成此报告,而不是我这里的基本循环 对于我的需求,每天都需要向我返回一个值,因此如果任何一天都没有销售,则返回一个0 任何一个Sum linq示例都没有解释如何可以包含where过滤器,因此我对如何获取过去几天的每日总金额感到困惑,如果没有销售额,则为0 谢谢你的帮助, 丰富的 我认为,如果枚举不包含某一天的数据,则无法返回该天的值。我所能想到的最好方法是为每天创建一个值为零的Order对象列表,并根据查询结果创建一个

我目前有以下代码来生成过去30天的销售报告。我想知道是否有可能使用linq一步生成此报告,而不是我这里的基本循环

对于我的需求,每天都需要向我返回一个值,因此如果任何一天都没有销售,则返回一个0

任何一个Sum linq示例都没有解释如何可以包含where过滤器,因此我对如何获取过去几天的每日总金额感到困惑,如果没有销售额,则为0

谢谢你的帮助, 丰富的


我认为,如果枚举不包含某一天的数据,则无法返回该天的值。我所能想到的最好方法是为每天创建一个值为零的Order对象列表,并根据查询结果创建一个union。这是我想到的。然而,我认为在每个组中循环,检查是否有任何一天被“跳过”,并为“跳过”的每一天返回零比在内存中创建自己的枚举更简单(除非您想要一个填充了“缺少的间隙”的枚举)。请注意,我基本上假设对于每个组,您希望对一天的所有值求和

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/>");
List zeroList=新列表();
while(startDate b.OrderDateTime>Convert.ToDateTime(startDate.Date+startTS)和&b.OrderDateTime 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.StartDate+”-到日期:“+totalSale.EndDate+”.Sales:“+String.Format(“{0:0.00}”,(十进制)totalSale.Sum)+“
”;
您可以按天对所有数据进行分组,并对这些分组进行求和。为了满足每天都有求和的要求,即使没有顺序,您也可以加入所有日期的列表,或者简单地使用循环来确保所有日期都包含在内。小提示:如果您按
Da进行比较,则无需明确设置时间teTime.Date
属性

以下是使用生成器函数的解决方案(取自MoreLinq项目):

公共静态部分类更可枚举
{
公共静态IEnumerable GenerateByIndex(函数生成器)
{
//在0…int.MaxValue上循环是一件痛苦的事情。最简单的方法是去独占,
//然后再次转到int.MaxValue。
对于(int i=0;ib.OrderDateTime>startDate.Date&&b.OrderDateTime startDate.AddDays(i)).Take(30);
var salesByDay=来自salesForPeriod中的s
按s.OrderDateTime.Date将s分组为g
选择new{Day=g.Key,totalSales=g.Sum(x=>(十进制)x.OrderPrice};
var query=所有天内从d开始
加入s在s日的salesByDay中。s日等于d
选择新建{Day=s.Day,totalSales=(s!=null)→s.totalSales:0m;
foreach(查询中的var项)
{
Response.Write(“日期:“+item.Day.ToString()”销售:“+String.Format(“{0:0.00}”),item.totalSales)+“
”; } } } }
我也提出了这个解决方案,但请求似乎包括没有返回行的天数。在这种情况下,该日期的值应为零。使用查询结果,我可以循环查看每天的结果,看看结果是否包含当天的和。如果是,我将返回和。否则,我将返回零。Howev呃,这不是所要求的一步LINQ查询。@吉米:谢谢,但我认为这一步更好。非常好的解决方案。我真的很喜欢这一步!很小的一点,在'var query'LINQ语句中,我会因为连接的'into j'部分而出错。如果我删除了它,代码会编译,但不会显示结果。如果我保持打开它,则不会“选择”行抛出错误,表示s未定义。有什么想法吗?再次感谢!感谢Johannes的更新。我做了一些调整后,就成功地实现了这一点。我已经在上面的问题中为其他任何有此问题的人提供了有效的var查询语句。再次感谢您的帮助:)
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 };
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/>");
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>");
            }


        }
    }
}