简体   繁体   中英

Linq to SQL C#: items between dates

I have 2 tables in my SQL server 2012:

Errors (id, cityID, centerID, date)

InspectionVisits (id, cityID, centerID, datePerformed)

I am trying to get the number of errors between inspection visits to see if there is an improvement in the center with the specific centerID and build a chart.

This is my code so far but I can't find out how I can write the where clause to get the number of errors between these inspection visits:

var errorsPerIV = from e in dc.Errors
                  where e.cityID == ctid && e.centerID == centerid 
                  group e by e.date.Date into g
                  join iv in dc.InspectionVisits on g.FirstOrDefault().cityID equals iv.cityID
                  where iv.centerID == g.FirstOrDefault().centerID
                  select new
                  {
                      Day = g.Key.Day + "/" +
                            g.Key.Month + "/" +
                            g.Key.Year,
                      Errors = g.Count()
                  };

Sample Case Something like: 5 errors between Inspection_Visit_1 and Inspection_Visit_2, 2 errors between Inspection_Visit_2 and Inspection_Visit_3 and 1 error between Inspection_Visit_3 and today.

EDIT

Maybe it could work if I show queries per day and mark only the inspection visits in the chart's x axis.

I'm not sure if there is a better way, but you could do something like the following

Suppose you have a class like

public class Summary
{
    public DateTime? PreviousInspection;
    public DateTime? NextInspection;
    public int Errors;
}

then you can get most of the information by having a query like

var errorsPerIV = (from e in dc.Errors
                      where e.cityID == ctid && e.centreID == centreid

                      // Find the date of the previous inspection (if any)
                      let previousInspection = (from i in dc.InspectionVisits where i.cityID == e.cityID && i.centreID == e.centreID && i.datePerformed <= e.date orderby i.datePerformed descending select i.datePerformed).FirstOrDefault()

                       // Find the date of the next inspection (if any)
                      let nextInspection     = (from i in dc.InspectionVisits where i.cityID == e.cityID && i.centreID == e.centreID && i.datePerformed >  e.date orderby i.datePerformed ascending  select i.datePerformed).FirstOrDefault()

                      group e by new { previousInspection , nextInspection } into results

                      orderby results.Key.previousInspection 
                      select new Summary
                      {
                          PreviousInspection = results.Key.previousInspection,
                          NextInspection     = results.Key.nextInspection ,
                          Errors             = results.Count()
                      })
                      .ToList();

However if there are no errors between two visits, then these visits will not appear in your list, so you need to find all the visits and see if there missing, ie something like

var inspectionsDates = (from i in InspectionVisits where i.cityID == ctid && i.centreID == centreid orderby i.datePerformed select i.datePerformed).ToList();

    for(int i=0; i< inspectionsDates.Count-1; i++)
    {
        if (!errorsPerIV.Any(a=>a.PreviousInspection == inspectionsDates[i]))
        {
            errorsPerIV.Add(new Summary() { PreviousInspection = inspectionsDates[i], NextInspection = inspectionsDates[i + 1], Errors = 0});
        }
    }

I think this would be easiest processed on the client side.

First you want to get the interesting InspectionVisits and order them by date, then convert to an Enumerable to pull them to the client:

var orderedIVs = InspectionVisits.Where(iv => iv.cityID == ctid && iv.centerID == centerid).Select(iv => iv.dateperformed).OrderBy(ivdp => ivdp).AsEnumerable();

Now using an extension method that processes along the Enumerable to compute a running value (it is called Scan because it is modeled after the APL Scan operator, which is like an Aggregate that returns all the intermediate values):

// TKey combineFn(T prev, T cur)
// TKey lastKeyFn(T cur)
public static IEnumerable<TResult> Scan<T, TResult>(this IEnumerable<T> src, Func<T, T, TResult> combineFn, Func<T, TResult> lastKeyFn) {
    using (var srce = src.GetEnumerator()) {
        if (srce.MoveNext()) {
            var prev = srce.Current;

            while (srce.MoveNext()) {
                yield return combineFn(prev, srce.Current);
                prev = srce.Current;
            }
            yield return lastKeyFn(prev);
        }
    }
}

You can compute the periods for the inspection visits:

var IVPeriods = orderedIVs.Scan((prev, cur) => new { Begin = prev, End = cur }, cur => new { Begin = cur, End = DateTime.Now });

Finally, with the periods you can count the Errors that occurred between each period:

var errorsPerIV = IVPeriods.Select(ivp => new { Day = ivp.Begin.Date, Count = Errors.Where(e => ivp.Begin <= e.date && e.date <= ivp.End).Count() });

If you want to process this on the server side, you must join the InspectionVisits table to itself so you can create the periods. I have not tested this with SQL server:

var orderedIVs = InspectionVisits.Where(iv => iv.cityID == ctid && iv.centerID == centerid).Select(iv => iv.dateperformed).OrderBy(ivdp => ivdp);

var IVPeriods = (from ivb in orderedIVs
                from ive in orderedIVs
                where ivb < ive
                group ive by ivb into iveg
                 select new { Begin = iveg.Key, End = iveg.OrderBy(iv => iv).First() })
                .Concat((from ivb in orderedIVs orderby ivb descending select new { Begin = ivb, End = DateTime.Now }).Take(1));

var errorsPerIV = IVPeriods.Select(ivp => new { Day = ivp.Begin.Date, Count = Errors.Where(e => ivp.Begin <= e.date && e.date <= ivp.End).Count() });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM