2013-05-29 76 views
7

我有如下表的时间间隔内只选择第一条记录

CREATE TABLE [dbo].[DeviceLogs](
    [DeviceLogId] [int] IDENTITY(1,1) NOT NULL, 

    [UserId] [nvarchar](50) NULL, 
    [LogDate] [datetime2](0) NULL, 
) 
GO 

数据样本

1  1 2013-05-29 11:05:15 //accepted (its the first occurance for userid 1) 
    2  1 2013-05-29 11:05:20 //discarded (within 5 mins from 1st record) 
    3  1 2013-05-29 11:07:56 //discarded (within 5 mins from 1st record) 
    4  1 2013-05-29 11:11:15 //accepted (after 5 mins from 1st occurance) 
    5  2 2013-05-29 11:06:05 //accepted (its the first occurance for userid 2) 
    6  2 2013-05-29 11:07:18 //discarded (within 5 mins from 1st record) 
    7  2 2013-05-29 11:09:38 //discarded (within 5 mins from 1st record) 
    8  2 2013-05-29 11:12:15 //accepted (after 5 mins from 1st occurance) 

我只想选择其中之后,从以前选择的记录5分钟已经发生的记录和包括数据集内的第一条记录

所需的输出是

1  1  2013-05-29 11:05:15 
4  1  2013-05-29 11:11:15 
5  2  2013-05-29 11:06:05 
8  2  2013-05-29 11:12:15 

我想要的GroupBy,但是没有得到日

db.DeviceLogs.GroupBy(g=>new {g.LogDate.Year, 
           g.LogDate.Month, 
           g.LogDate.Day, 
           g.LogDate.Hour, 
           g.LogDate.Minutes, 
           g.UserID}) 
      .Select(s=>new {UserID=s.Key.UserID, s.???}); 

预先感谢您。

+0

,我不认为你可以在LINQ到SQL做到这一点。你可以通过滥用带有自定义比较的'GroupBy',或者使用类似LINQ的自定义方法(尽管这不再使用LINQ)来实现LINQ到对象。 – Rawling

+0

我需要一点澄清。五分钟后的所有参赛作品是否包含在内,或者抽样是否继续?如果继续,那么基于第一个条目的五分钟窗口会保留吗,或者,每个窗口是否从最后一个接受者开始? – Jodrell

+0

@Jodrell:是的,只有窗口是5分钟,条目是几个月和一年。其次,五分钟的窗口仍然基于该特定用户的数据集中的第一个条目。 – Marshal

回答

5
var result = 
    from log in db.DeviceLogs 
    let byId = 
     db.DeviceLogs.Where(item => item.UserId == log.UserId) 
    let first = 
     byId.First(item => item.LogDate == byId.Min(min => min.LogDate)) 
    where 
     log.Equals(first) || (log.LogDate - first.LogDate).Minutes > 5 
    select log; 
+0

这不太正确 –

+0

因此,让我们知道什么是不正确的。 –

+0

1秒,请。似乎错误在于,您正在比较所有记录中的MinimumDate元素,而您应该与用户特定的最小值进行比较。请见我的回答。 –

2

好的,如何。

var firstDates = db.DeviceLogs.GroupBy(d => d.UserId).ToDictionary(
    g => g.Key, 
    g => g.OrderBy(d => d.LogDate).First().LogDate); 

db.DeviceLogs.GroupBy(g => new 
    { 
    v = Math.Floor(SqlMethods.DateDiffMinute(firstDates[d.UserId], g.LogDate)/5), 
    u = g.UserID 
    }).Select(s => s.OrderBy(s => s.LogDate).First()); 

我不确定您可以使用linq to SQL在一个查询中执行此操作。如果分钟数超过32位整数的最大值,则存在潜在问题。

+0

''''''''我已经删除了我的dv,因为这不像我第一眼看到的那样严重错误,但它只会得到第一个项目每个用户每绝对分钟而不是每个用户每滑动五分钟窗口的第一项。 – Rawling

+0

@Rawling,澄清后修改。 – Jodrell

+0

这在GroupBy语句中给出错误:'无效的匿名类型成员声明符。匿名类型成员必须声明为成员分配,简单名称或成员访问权。“# – Marshal

1

我可以建议你的SQL解决方案:

SELECT [DeviceLogId],[UserId],[LogDate] FROM (
    SELECT *, 
      (SELECT top 1 [LogDate] FROM DeviceLogs t2 
         WHERE datediff(minute,t2.logDate,t1.logDate)>5 
         ORDER BY [LogDate] DESC) prev, 
      (SELECT TOP 1 [Logdate] FROM DeviceLogs t3 
       WHERE t3.[LogDate]= 
        (SELECT MIN([LogDate]) 
        FROM DeviceLogs t4 
        WHERE t4.[UserId]=t1.[UserId])) first 
    FROM DeviceLogs t1) tres 
WHERE prev IS NOT NULL OR first=logdate 

见提琴手http://sqlfiddle.com/#!6/fa74e/50

+0

感谢voo ..但我很难将它转换为LINQ:P – Marshal

1

我不确定你可以用一个单一的LINQ语句来完成这一点,因为你需要记住的日期时间最后一个记录被放弃。 可以使用迭代器块,像这样:

private static readonly TimeSpan MinimumTimeSpan = new TimeSpan(0,5,0); 

IEnumerable<Record> getSparseRecords(IEnumerable<Record> allRecords) 
{ 
    DateTime previous = DateTime.MinValue; 
    foreach(var record in allRecords) 
    { 
     TimeSpan dif = record.DateTime - previous; 
     if (dif >= MinimumTimeSpan) 
     { 
      previous = record.DateTime; 
      yield return record; 
     } 
    } 
} 

其中记录将是一类来表示一个单独的记录,它包括DateTime类型(见here)的一个DateTime属性。如果您的LogDate属性属于不同类型,则可能需要稍微更改一下代码。

1

这是我的解决方案

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<DeviceLog> list = new List<DeviceLog> 
      { 
       new DeviceLog() { Id = 1, UserId = 1, LogDate = DateTime.Parse("2013-05-29 11:05:15") }, 
       new DeviceLog() { Id = 2, UserId = 1, LogDate = DateTime.Parse("2013-05-29 11:05:20") }, 
       new DeviceLog() { Id = 3, UserId = 1, LogDate = DateTime.Parse("2013-05-29 11:07:56") }, 
       new DeviceLog() { Id = 4, UserId = 1, LogDate = DateTime.Parse("2013-05-29 11:11:15") }, 

       new DeviceLog() { Id = 5, UserId = 2, LogDate = DateTime.Parse("2013-05-29 11:06:05") }, 
       new DeviceLog() { Id = 6, UserId = 2, LogDate = DateTime.Parse("2013-05-29 11:07:18") }, 
       new DeviceLog() { Id = 7, UserId = 2, LogDate = DateTime.Parse("2013-05-29 11:09:38") }, 
       new DeviceLog() { Id = 8, UserId = 2, LogDate = DateTime.Parse("2013-05-29 11:12:15") }, 
      }; 

     list = list.Where(l => (l.Id == list.Where(g => g.UserId == l.UserId).Min(h => h.Id)) 
      || (l.LogDate - list.Where(g => g.UserId == l.UserId).OrderBy(m => m.Id).First().LogDate).Minutes > 5).ToList(); 


    } 


} 

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

    public int UserId { get; set; } 

    public DateTime LogDate { get; set; } 

}