2010-05-16 5 views
2

J'ai actuellement le code suivant pour générer un rapport de vente au cours des 30 derniers jours. J'aimerais savoir s'il serait possible d'utiliser linq pour générer ce rapport en une seule étape au lieu de la boucle plutôt basique que j'ai ici.Utiliser Linq to SQL pour générer le rapport des ventes

Pour mes besoins, chaque jour doit me retourner une valeur, donc s'il n'y a aucune vente pour un jour, alors un 0 est retourné. L'un des exemples Sum linq n'explique pas comment il serait possible d'inclure un filtre where, donc je suis confus sur la façon d'obtenir le montant total par jour, ou un 0 si aucune vente, pour la dernière fois. jours que je traverse.

Merci pour votre aide, Rich

//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); 
     } 
    } 

EDIT: Johannes réponse était une excellente façon de gérer ma requête. Vous trouverez ci-dessous un ajustement au code pour le faire fonctionner pour cet exemple au cas où quelqu'un d'autre aurait ce problème. Cela va effectuer une jointure externe à partir de la table allDays et retourner des valeurs 0 lorsqu'il n'y a pas de ventes pour ce jour.

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 }; 

Répondre

4

Vous pouvez regrouper toutes vos données par jour et cumuler ces groupes. Pour satisfaire à l'exigence d'avoir une somme pour chaque jour, même ceux sans ordre, vous pouvez joindre une liste de toutes les dates ou simplement utiliser une boucle pour vous assurer que toutes les dates sont incluses. Petit conseil: Vous n'avez pas besoin de configurer les heures explicitement si vous comparez par les propriétés DateTime.Date.

est ici la solution en utilisant une fonction de générateur (extrait du projet 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>"); 
      } 


     } 
    } 
} 
+0

je suis venu avec cette solution aussi, mais la demande semble inclure jours pour lesquels aucune ligne ne sont renvoyés. Dans un tel cas, la valeur devrait être nulle pour un tel jour. En utilisant le résultat de la requête, je pourrais parcourir chaque jour et vérifier si le résultat contient une somme pour ce jour. Si oui, je vais retourner la somme. Sinon, je reviendrai à zéro. Cependant, il ne s'agit pas d'une requête LINQ en une seule étape, comme demandé. –

+0

Je vois votre édition. Très agréable. –

+0

@Jimmy: Merci, mais je pense que celui-ci est encore plus agréable. –

0

Je pense que si votre énumération ne contient pas de données pour un jour, vous ne pouvez pas retourner des valeurs pour ce jour-là. Le mieux que je puisse penser est de créer une liste d'objets Order avec une valeur nulle pour chaque jour et créer une union avec le résultat de votre requête. Voici ce que j'ai trouvé. Cependant, je pense faire une boucle dans chaque groupe, vérifier si un jour est "sauté", et retourner zéro pour chaque jour qui est "sauté" est plus simple que de créer votre propre énumération en mémoire (sauf si vous voulez une énumération avec les "rempli". Veuillez noter que je suppose essentiellement que pour chaque groupe, vous voulez additionner toutes les valeurs pour un seul jour.

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/>");