如何在LINQ中协调第一个和最后一个记录

时间:2013-06-21 07:39:06

标签: c# .net sql linq logic

我的SQL数据库中有一个表,可以跟踪员工的时间和时间。典型的记录看起来像这样

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只是一个自动增量列
设备是他们点击他们的工作卡,进入/退出的设备 DateTime是他们点击员工卡的时间

我想知道,在一天结束时,当我生成一份报告时,我如何协调他们的及时时间,以便输出可能如下所示:

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

注意事项:
- 请注意,员工1有2条“InReader”记录,我想采取较早的记录
- 员工2有2个“OutReader”记录,我想只记录他的最新记录

如何使用LINQ协调IN和OUT记录? (如果在LINQ中不可能,则为TSQL)

5 个答案:

答案 0 :(得分:1)

我为您提供了此查询,并在LinqPad中进行了测试。我会给你完整的代码,以便你自己试试。

查询本身:

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

完整的代码示例:

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

答案 1 :(得分:0)

 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
                }

上面那样的东西应该可以解决问题。

答案 2 :(得分:0)

使用Min, Max聚合返回序列中最小或最大的元素,并使用GroupBy进行排序。

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

我认为您知道您员工的身份证件(或者您可能正在查看清单)以及您生成报告的日期,因此您首先需要做的是让您的员工进出时间某事的一天像这样:

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

您还可以轻松地重写此查询以获取当天所有员工的时间,并最近按EmployeeId进行过滤(这取决于您的情况更好)。

之后,您可以轻松地从您的员工进出报告所需日期的进出时间,如下所示:

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