Null Reference
Null Reference

Reputation: 11340

How to reconcile first and last record in LINQ

I have a table in my SQL database that tracks an employee's in time and out time. A typical record would look like this

Id           Device               DateTime                    EmployeeId    
-------------------------------------------------------------------------
1            InReader             2013/05/05 08:00:00         1
2            InReader             2013/05/05 08:00:05         1
3            InReader             2013/05/05 08:01:00         2
4            InReader             2013/05/05 08:02:00         3
5            InReader             2013/05/05 08:03:00         4
6            OutReader            2013/05/05 17:00:00         1
7            OutReader            2013/05/05 17:05:05         2
8            OutReader            2013/05/05 17:05:10         2
9            OutReader            2013/05/05 17:10:00         3
10           OutReader            2013/05/05 17:30:00         4

Id is just an auto increment column
Device is the device that they tapped their staff card on, to clock in/or out
DateTime is the time that they tapped their staff card

I would like to know, at the end of the day, when I'm generating a report, how can I reconcile their in-time with out-time, such that the output may look like this:

Employee Id            In time                    Out time
-----------------------------------------------------------------------
1                      2013/05/05 08:00:00        2013/05/05 17:00:00
2                      2013/05/05 08:01:00        2013/05/05 17:05:10
3                      2013/05/05 08:02:00        2013/05/05 17:10:00
4                      2013/05/05 08:03:00        2013/05/05 17:30:00

Caveats:
- Note that Employee 1 has 2 records for "InReader", I would want to take the earlier record
- Employee 2 has 2 records for "OutReader", I would want to take only his latest record

How do I reconcile the IN and OUT records using LINQ? (or TSQL if it's not possible in LINQ)

Upvotes: 3

Views: 3539

Answers (5)

Mr_CRivera
Mr_CRivera

Reputation: 239

I suppose that you know your employee's ID (or may be you are going through a list) and the date at which you're generating your report, so first thing you need to do is to get your employee's in and out times of the day with sth. like this:

 //first second of the day
 DateTime firstSecondOfTheDay = dateToCheck.Subtract(dateToCheck.TimeOfDay);
 //last second of the day 
 TimeSpan endOfDay = new TimeSpan(23, 59, 59);
 DateTime lastSecondOfTheDay = firstSecondOfTheDay.Add(endOfDay);
 var employeeDayInOut = from emp in context.Employess
                        where (emp.DateTime >= firstSecondOfTheDay) &
                              (emp.DateTime <= lastSecondOfTheDay) &
                              (emp.EmployeeId == idToCheck)
                        select emp;

You could also easily rewrite this query in order to obtain all employees times of the day, and filter by EmployeeId lately (it depends on what is better for your case).

And after that you can obtain easily from your employee's in and out times of the day the date you need for your report like this:

  employeeDayInOut.Max(emp => emp.DateTime);
  employeeDayInOut.Min(emp => emp.DateTime);

Upvotes: 0

Jordy Langen
Jordy Langen

Reputation: 3591

I made you this query, and tested in LinqPad. I'll give you the full code so you can try it yourself.

The query itself:

tracks.GroupBy(x => x.EmployeeId)
      .Select(x => new 
            {
                EmployeeId = x.Key,
                InTime = x.FirstOrDefault(y => y.Device.Equals("InReader")).DateTime,
                OutTime = x.LastOrDefault(y => y.Device.Equals("OutReader")).DateTime
            })

The full code sample:

void Main()
{
    var tracks = new[]
    {
        new Track{Id = 1, Device = "InReader", DateTime = new DateTime(2013,5,5,8,0,0), EmployeeId = 1},
        new Track{Id = 2, Device = "InReader", DateTime = new DateTime(2013,5,5,8,0,5), EmployeeId = 1},
        new Track{Id = 3, Device = "InReader", DateTime = new DateTime(2013,5,5,8,1,0), EmployeeId = 2},
        new Track{Id = 4, Device = "InReader", DateTime = new DateTime(2013,5,5,8,2,0), EmployeeId = 3},
        new Track{Id = 5, Device = "InReader", DateTime = new DateTime(2013,5,5,8,3,0), EmployeeId = 4},

        new Track{Id = 6, Device = "OutReader", DateTime = new DateTime(2013,5,5,17,0,0), EmployeeId = 1},
        new Track{Id = 7, Device = "OutReader", DateTime = new DateTime(2013,5,5,17,5,5), EmployeeId = 2},
        new Track{Id = 8, Device = "OutReader", DateTime = new DateTime(2013,5,5,17,5,10), EmployeeId = 2},
        new Track{Id = 9, Device = "OutReader", DateTime = new DateTime(2013,5,5,17,10,0), EmployeeId = 3},
        new Track{Id = 10, Device = "OutReader", DateTime = new DateTime(2013,5,5,17,30,0), EmployeeId = 4},
    };

        // the Query
        tracks
    .GroupBy(x => x.EmployeeId)
    .Select(x => new 
        {
            EmployeeId = x.Key,
            InTime = x.FirstOrDefault(y => y.Device.Equals("InReader")).DateTime,
            OutTime = x.LastOrDefault(y => y.Device.Equals("OutReader")).DateTime
        })
}

public class Track
{
    public int Id { get; set; }

    public string Device { get; set; }

    public DateTime DateTime { get; set; }

    public int EmployeeId { get; set; }
}

Upvotes: 2

Ahmed KRAIEM
Ahmed KRAIEM

Reputation: 10427

var result = table.Where(e => e.Device == "InReader")
                    .GroupBy(e => e.EmployeeId)
                        .Select(g => g.OrderBy(d => d.DateTime).First())
            .Join(
            table.Where(e => e.Device == "OutReader")
                    .GroupBy(e => e.EmployeeId)
                        .Select(g => g.OrderByDescending(d => d.DateTime).First()), 
            t => t.EmployeeId, t => t.EmployeeId,(t, i) => new { Id = t.EmployeeId, In = t.DateTime, Out = i.DateTime });

Upvotes: 0

S.N
S.N

Reputation: 5140

Use Min, Max aggregate to returns the smallest or largest element in the sequence and use GroupBy for ordering.

var result=YourTableRowCollection.GroupBy(x=>x.EmployeeId)
                            .Select(x=>new { EmployeeId=x.Key,
                                             InTime=x.Min(t=>DateTime.Parse(t.InTime)).ToString(),
                                             OutTime=x.Max(t=>DateTime.Parse(t.OutTime)).ToString()});

Upvotes: 1

b_meyer
b_meyer

Reputation: 604

 var x = from current in context.Employess
                let firstRecord = current.Where(c=> c.Track.Device == "InReader").OrderBy(c => c.DateTime).First()
                let lastRecord = current.Where(c=> c.Track.Device == "OutReader").OrderBy(c => c.DateTime).Last()
                select{
                    // do something
                }

Something like that above should do the trick.

Upvotes: 0

Related Questions