Reputation: 11340
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
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
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
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
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
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