2

C# で特定の間隔でデータベースから値をフェッチしたいのですが、そのために単一のクエリが必要です。これは私のデータベースがどのように見えるかです

ID SensorId 値 CreatedOn   
------------------------------------------------      
1 8 33.5 2012 年 11 月 15 日午後 5 時 48 分        
2 5 49.2 2012 年 11 月 15 日午後 5 時 48 分
3 8 33.2 2012 年 11 月 15 日午後 5 時 49 分  
4 5 48.5 2012 年 11 月 15 日午後 5 時 49 分  
5 8 31.8 2012 年 11 月 15 日午後 5 時 50 分
6 5 42.5 2012 年 11 月 15 日午後 5 時 50 分
7 8 36.5 2012 年 11 月 15 日午後 5 時 51 分
8 5 46.5 2012 年 11 月 15 日午後 5 時 51 分
9 8 39.2 2012 年 11 月 15 日午後 5 時 52 分
10 5 44.4 2012 年 11 月 15 日 午後 5 時 52 分
11 8 36.5 15-11-2012 5:53 午後
12 5 46.5 2012 年 11 月 15 日午後 5 時 53 分
13 8 39.2 2012 年 11 月 15 日午後 5 時 54 分
14 5 44.4 2012 年 11 月 15 日午後 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. センサーごとに1分間に複数の読み取り値が表示されることはありません。
  3. 1分未満の間隔を処理する必要はありません。

これらはおそらく誤った仮定であるため、別のアプローチが必要です。まず、クエリを実行しているすべての時点のマップを作成します。次に、そのポイントまたはそれ以前の各センサーから最後の読み取り値を取得します。

これは、純粋なlinq-to-objectsでどのように実行できるかを示す完全な単体テストです。クエリをlinq-to-sqlで機能させるには、クエリに若干の変更が必要になる場合がありますが、これは正しいアプローチです。ご提供いただいた正確なサンプルデータを使用しました。

余談ですが、CreatedOnの日付をUTCで記録しているか、夏時間の「フォールバック」遷移中にセンサーの読み取り値があいまいになることを願っています。UTCでDateTimeとして記録するか、DateTimeOffsetを使用して記録する必要があります。どちらも瞬間時間の適切な表現です。.KindがLocalまたはUnspecificedの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

データベースへの2回の呼び出しでそれを行う方法は次のとおりです(テストされていません):

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 に答える