【问题标题】:Select only first record within a time interval仅选择时间间隔内的第一条记录
【发布时间】:2013-05-29 08:12:43
【问题描述】:

我有下表

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

提前谢谢你。

【问题讨论】:

  • 我认为您不能在 LINQ-to-SQL 中执行此操作。您可以在 LINQ-to-objects 中通过自定义比较或自定义类 LINQ 方法滥用GroupBy 来实现(尽管这不再是真正使用 LINQ)。
  • 我需要澄清一下。是否将包括五分钟后的所有条目,或者采样是否继续?如果继续,五分钟的窗口是否仍然基于第一个条目,或者每个窗口是否从最后一个接受者开始?
  • @Jodrell:是的,只有 Window 是 5 分钟,条目是几个月和一年。其次,根据该特定用户的数据集中的第一个条目,保留五分钟的窗口。
  • 您的only records which have occured after 5 mins from previous selected record 似乎与five minute window remain based on the first entry 不一致。

标签: c# sql linq linq-to-sql


【解决方案1】:
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;

【讨论】:

  • 1 秒,请。似乎错误是您正在与所有记录中的 MinimumDate 元素进行比较,而您应该与用户特定的最小值进行比较。请看我的回答。
  • .Where(item => item.UserId == log.UserId) 将搜索集限制为用户:)
  • 这是您答案的更改版本,并在 linqpad 中输出,但我不否认您的答案是基础。我确实给了你一个赞成票
  • db.DeviceLogs.Min(min => min.LogDate) 不受限制,这只是最小的日期
  • 你几乎没有和我做同样的事情,只是把 id 过滤的东西放在一个额外的变量中......你的答案和我的原来一样。
【解决方案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 位整数的最大值,则存在潜在问题。

【讨论】:

  • 我已经删除了我的 dv,因为这并不像我乍一看那样严重错误,但它只会获得每个用户每绝对分钟的第一个项目,而不是每个用户的第一个项目-user-per-sliding-五分钟窗口。
  • @Rawling,澄清后修改。
  • 这在 GroupBy 语句中出现错误:Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
【解决方案3】:

我不确定您是否可以使用单个 LINQ 语句来执行此操作,因为您需要记住最后产生的记录的 DateTime。 您可以像这样使用迭代器块:

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

其中 Record 是表示单个记录的类,其中包括类型为 DateTime 的 DateTime 属性(请参阅here)。如果您的 LogDate 属性属于不同类型,您可能需要稍微更改代码。

【讨论】:

    【解决方案4】:

    这是我的解决方案

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

    【讨论】:

      【解决方案5】:

      我可以建议你一个 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

      【讨论】:

      • 谢谢voo..但我很难将它转换为LINQ:P
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-15
      • 2014-09-12
      • 2015-03-13
      • 1970-01-01
      • 2020-09-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多