Creating trend data from data that has start and e

2019-08-22 05:18发布

问题:

I'm using C#, NHibernate, Fluent NHibernate and LINQ (repository pattern).

Fictional story on what I want to do

Say I have a car parking place, so I have a car that is renting a space from date until date. This becomes one entry into the database.

I would like to find out how many cars there were for each day over the period of last 30 days. So data that can be used in a trend chart.

Description

What I have is entries in the database that are like so:

Entry:
--------
ID
StartDate
EndDate

I need to find out how many entries there were each day for a time period (say last 30 days).

If I have say this data in the database:

ID | Start      | End
---|------------|----------
1  | 2013-01-01 | 2013-01-01
2  | 2013-01-02 | 2013-01-02
3  | 2013-01-02 | 2013-01-03 <-- NOTICE
4  | 2013-01-03 | 2013-01-03
5  | 2013-01-03 | 2013-01-05 <-- NOTICE
6  | 2013-01-04 | 2013-01-05

I have date range 2013-01-01 until 2013-01-05.

I except the result:

Date       | Value | Entry IDs (not part of the result, only here for clarity)
-----------|-------|-----------
2013-01-01 | 1     | 1
2013-01-02 | 2     | 2 3
2013-01-03 | 3     | 3 4 5
2013-01-04 | 2     | 5 6
2013-01-05 | 2     | 5 6

I currently only group by startdate and then get the numbers, but this is invalid now because of this new requirement that was added with enddate.

I could do a for each day in that range, try to find entries that match in that, but I'm thinking that would be too expensive on the database or web service.

Advices?

回答1:

Maybe something like this:

var start = new DateTime(2013, 01, 01);
var end = DateTime(2013, 01, 05);

var q = d.Query()
         .Where(x =>
             x.Date >= start
             && end >= x.Date) // there you filter for range 
         .GroupBy(x => x.Date); // use .Day to group by day

Start time and end time are inclusive.

EDIT

I've found this (actually i've a similar case in my project):

var start = new DateTime(2013, 1, 1);
var end = new DateTime(2013, 1, 5);
var current = start;
var ranges = new List<DateTime>();

while (current <= end)
{
    ranges.Add(current);

    current = current.AddDays(1);
}

var res = ranges.Select(x => new
{
    date = x,
    entries = entries.Where(e => e.Start <= x && x <= e.End).ToList()
}).ToList();

Try test it and maybe you can acomodate to your case (note that filtering starts by the date range).



回答2:

You can use the Time Period Library for .NET to calculate the trend data:

// ----------------------------------------------------------------------
class CarPeriod : DayTimeRange
{

  // --------------------------------------------------------------------
  public CarPeriod( int id, DateTime start, DateTime end ) :
    this( id, start, end.Date.AddDays( 1 ).Subtract( start.Date ).Days )
  {
  } // CarPeriod

  // --------------------------------------------------------------------
  public CarPeriod( int id, DateTime start, int days ) :
    base( start.Year, start.Month, start.Day, days )
  {
    Id = id;
  } // CarPeriod

  // --------------------------------------------------------------------
  public int Id { get; private set; }

} // class CarPeriod

// ----------------------------------------------------------------------
[Sample( "DaysTrendData" )]
public void DaysTrendData()
{
  // periods
  ITimePeriodCollection carPeriods = new TimePeriodCollection();
  carPeriods.Add( new CarPeriod( 1, new DateTime( 2013, 1, 1 ), new DateTime( 2013, 1, 1 ) ) );
  carPeriods.Add( new CarPeriod( 2, new DateTime( 2013, 1, 2 ), new DateTime( 2013, 1, 2 ) ) );
  carPeriods.Add( new CarPeriod( 3, new DateTime( 2013, 1, 2 ), new DateTime( 2013, 1, 3 ) ) );
  carPeriods.Add( new CarPeriod( 4, new DateTime( 2013, 1, 3 ), new DateTime( 2013, 1, 3 ) ) );
  carPeriods.Add( new CarPeriod( 5, new DateTime( 2013, 1, 3 ), new DateTime( 2013, 1, 5 ) ) );
  carPeriods.Add( new CarPeriod( 6, new DateTime( 2013, 1, 4 ), new DateTime( 2013, 1, 5 ) ) );

  Days testDays = new Days( new DateTime( 2013, 1, 1 ), 5 );
  foreach ( Day testDay in testDays.GetDays() )
  {
    Console.Write( "Day: " + testDay + ": " );
    foreach ( CarPeriod carPeriod in carPeriods )
    {
      if ( carPeriod.IntersectsWith( testDay ) )
      {
        Console.Write( carPeriod.Id + " " );
      }
    }
    Console.WriteLine();
  }
} // DaysTrendData