2

我想在 C# 中以特定间隔从数据库中获取值,并且需要一个查询。这就是我的数据库的样子

Id SensorId 值 CreatedOn   
------------------------------------------------      
1 8 33.5 15-11-2012 下午 5:48        
2 5 49.2 15-11-2012 下午 5:48
3 8 33.2 15-11-2012 下午 5:49  
4 5 48.5 2012 年 11 月 15 日下午 5:49  
5 8 31.8 15-11-2012 下午 5:50
6 5 42.5 15-11-2012 下午 5:50
7 8 36.5 15-11-2012 下午 5:51
8 5 46.5 15-11-2012 下午 5:51
9 8 39.2 15-11-2012 下午 5:52
10 5 44.4 15-11-2012 下午 5:52
11 8 36.5 15-11-2012 下午 5:53
12 5 46.5 15-11-2012 下午 5:53
13 8 39.2 15-11-2012 下午 5:54
14 5 44.4 15-11-2012 下午 5:54
………………………………………………………………………………………………………………

间隔以分钟为单位。所以,如果间隔是 10 分钟,那么我们需要 5:48、5:58、6:08 等处的值...

我尝试使用 while 循环来执行此操作,但是当我向数据库发送多个查询时会花费大量时间。

有没有办法在单个查询中获取数据?

4

4 回答 4

3

您可以使用datepart模数来获取匹配的行(例如,@interval = 10、@offset = 8):

SELECT * FROM table
WHERE datepart(minute, CreatedOn) % @interval = @offset

编辑

请注意,以上不是按间隔选择的一般解决方案。它将跨小时(因此跨天)工作,间隔为 2、3、4、5 ......任何分为 60 的分钟间隔。

如果你想使用一个奇怪的间隔,比如 7 分钟,那么你必须定义一个间隔的开始时间并计算每行的总分钟数,包括小时/天。此时,您最好根据计算相关间隔的用户定义函数在表上创建一个索引计算列。

于 2012-11-29T05:33:38.737 回答
1

这是你可以做到的,解释包含在代码的注释中:

/*We want 10-minute intervals starting 
  from minimum date to next day same time*/
DECLARE @startDateTime DATETIME = (
    SELECT  MIN(CreatedOn)
    FROM    #yourTable
)
DECLARE @endDateTime DATETIME = DATEADD(DAY, 1, @startDateTime)

DECLARE @startDateTimeTable TABLE (dt DATETIME)
INSERT @startDateTimeTable VALUES (@startDateTime)

/*Create a table that contains relevant datetimes (10-minute 
  intervals from starting date to end date)*/
;WITH a AS (
    SELECT  dt
    FROM    @startDateTimeTable
    UNION   ALL
    SELECT  DATEADD(MINUTE, 10, a.dt)
    FROM    a
    JOIN    @startDateTimeTable b ON a.dt <= @endDateTime
)
SELECT  *
INTO    #requiredDateTimes
FROM    a
OPTION  (MAXRECURSION 32767)

/*Now join data table to datetime table to 
  filter out only records with datetimes that we want*/
SELECT  *
FROM    #yourTable a
JOIN    #requiredDateTimes b ON
        a.CreatedOn = b.dt

这是一个SQL 小提琴

于 2012-11-29T08:48:41.490 回答
0

任何建议使用模数 (%) 的答案都做了几个假设:

  1. 您将始终在有问题的确切时间读取每个传感器的读数
  2. 每个传感器一分钟内的读数永远不会超过一个。
  3. 您将永远不必处理小于一分钟的间隔。

这些可能是错误的假设,因此您需要一种不同的方法。首先,制作您要查询的所有时间点的地图。然后在该点或之前从每个传感器获取最后一个读数。

这是一个完整的单元测试,展示了如何在纯 linq-to-objects 中完成它。您可能需要对查询进行一些小的更改才能使其在 linq-to-sql 中工作,但这是正确的方法。我使用了您提供的确切样本数据。

顺便说一句 - 我希望您在 UTC 中记录您的 CreatedOn 日期,否则在夏令时“回退”转换期间您的传感器读数会模棱两可。您需要以 UTC 格式记录为 DateTime,或使用 DateTimeOffset。两者都是瞬时时间的适当表示。带有 .Kind 的 Local 或 Unspecified 的 DateTime 只是日历时间的有效表示,不适用于传感器读数。

[TestClass]
public class LinqIntervalQueryTest
{
    public class Item
    {
        public int Id { get; set; }
        public int SensorId { get; set; }
        public double Value { get; set; }
        public DateTime CreatedOn { get; set; }
    }

    [TestMethod]
    public void Test()
    {
        var data = new[]
            {
                new Item { Id = 1, SensorId = 8, Value = 33.5, CreatedOn = new DateTime(2012, 11, 15, 17, 48, 0, DateTimeKind.Utc) },
                new Item { Id = 2, SensorId = 5, Value = 49.2, CreatedOn = new DateTime(2012, 11, 15, 17, 48, 0, DateTimeKind.Utc) },
                new Item { Id = 3, SensorId = 8, Value = 33.2, CreatedOn = new DateTime(2012, 11, 15, 17, 49, 0, DateTimeKind.Utc) },
                new Item { Id = 4, SensorId = 5, Value = 48.5, CreatedOn = new DateTime(2012, 11, 15, 17, 49, 0, DateTimeKind.Utc) },
                new Item { Id = 5, SensorId = 8, Value = 31.8, CreatedOn = new DateTime(2012, 11, 15, 17, 50, 0, DateTimeKind.Utc) },
                new Item { Id = 6, SensorId = 5, Value = 42.5, CreatedOn = new DateTime(2012, 11, 15, 17, 50, 0, DateTimeKind.Utc) },
                new Item { Id = 7, SensorId = 8, Value = 36.5, CreatedOn = new DateTime(2012, 11, 15, 17, 51, 0, DateTimeKind.Utc) },
                new Item { Id = 8, SensorId = 5, Value = 46.5, CreatedOn = new DateTime(2012, 11, 15, 17, 51, 0, DateTimeKind.Utc) },
                new Item { Id = 9, SensorId = 8, Value = 39.2, CreatedOn = new DateTime(2012, 11, 15, 17, 52, 0, DateTimeKind.Utc) },
                new Item { Id = 10, SensorId = 5, Value = 44.4, CreatedOn = new DateTime(2012, 11, 15, 17, 52, 0, DateTimeKind.Utc) },
                new Item { Id = 11, SensorId = 8, Value = 36.5, CreatedOn = new DateTime(2012, 11, 15, 17, 53, 0, DateTimeKind.Utc) },
                new Item { Id = 12, SensorId = 5, Value = 46.5, CreatedOn = new DateTime(2012, 11, 15, 17, 53, 0, DateTimeKind.Utc) },
                new Item { Id = 13, SensorId = 8, Value = 39.2, CreatedOn = new DateTime(2012, 11, 15, 17, 54, 0, DateTimeKind.Utc) },
                new Item { Id = 14, SensorId = 5, Value = 44.4, CreatedOn = new DateTime(2012, 11, 15, 17, 54, 0, DateTimeKind.Utc) },
            };

        var interval = TimeSpan.FromMinutes(3);
        var startDate = data.First().CreatedOn;
        var endDate = data.Last().CreatedOn;

        var numberOfPoints = (int)((endDate - startDate + interval).Ticks / interval.Ticks);
        var points = Enumerable.Range(0, numberOfPoints).Select(x => startDate.AddTicks(interval.Ticks * x));

        var query = from item in data
                    group item by item.SensorId
                    into g
                    from point in points
                    let itemToUse = g.LastOrDefault(x => x.CreatedOn <= point)
                    orderby itemToUse.CreatedOn, g.Key
                    select new
                        {
                            itemToUse.CreatedOn,
                            itemToUse.Value,
                            SensorId = g.Key
                        };

        var results = query.ToList();

        Assert.AreEqual(6, results.Count);

        Assert.AreEqual(data[1].CreatedOn, results[0].CreatedOn);
        Assert.AreEqual(data[1].Value, results[0].Value);
        Assert.AreEqual(data[1].SensorId, results[0].SensorId);

        Assert.AreEqual(data[0].CreatedOn, results[1].CreatedOn);
        Assert.AreEqual(data[0].Value, results[1].Value);
        Assert.AreEqual(data[0].SensorId, results[1].SensorId);

        Assert.AreEqual(data[7].CreatedOn, results[2].CreatedOn);
        Assert.AreEqual(data[7].Value, results[2].Value);
        Assert.AreEqual(data[7].SensorId, results[2].SensorId);

        Assert.AreEqual(data[6].CreatedOn, results[3].CreatedOn);
        Assert.AreEqual(data[6].Value, results[3].Value);
        Assert.AreEqual(data[6].SensorId, results[3].SensorId);

        Assert.AreEqual(data[13].CreatedOn, results[4].CreatedOn);
        Assert.AreEqual(data[13].Value, results[4].Value);
        Assert.AreEqual(data[13].SensorId, results[4].SensorId);

        Assert.AreEqual(data[12].CreatedOn, results[5].CreatedOn);
        Assert.AreEqual(data[12].Value, results[5].Value);
        Assert.AreEqual(data[12].SensorId, results[5].SensorId);
    }
}
于 2012-11-29T21:51:40.567 回答
0

以下是在两次调用数据库中的方法(未经测试):

int interval = 10;
DateTime firstDate = db.Items.Select(x => x.CreatedOn).Min();
var items = db.Items.Where(x => (x.CreatedOn - firstDate).TotalMinutes % interval == 0).ToList();
于 2012-11-29T06:32:26.927 回答