如何在此时间间隔内连接多个"最高"查询调用一个查询?

时间:2014-05-19 15:04:52

标签: sql-server performance entity-framework linq-to-entities query-optimization

假设我们有(剥离)实体:

Message
{
    int MessageID;
    int DeviceID;
    DateTime Timestamp;
    string Value;
};

Device
{
   int DeviceID;
   string name;
   byte Type;
};

我们有10 000台设备,每两台每两分钟发送最多5条状态消息两年,这就是数据库。现在我们想要,比如说 - 500个选择的Type == 254的设备可以创建实时报告,它可以让你选择一段时间,间隔(每天,每2小时,5分钟或5秒)等等每个结果时间戳都会在此时间戳之前收到最后一条消息。我已尝试创建此查询,但无法一次查询一个时间戳(时刻):

DateTime timestamp = // from argument

var query = from message in this.Messages
    .Include(msg => msg.Device)
    where message.Timestamp <= timestamp
    && msg.Device.Type == 254
    group message by message.DeviceID into g
    select (from message in g
        orderby message.Timestamp descending
        select message).FirstOrDefault();

它的作用:它限制了搜索的时间戳(仅在&#34;时间戳之前得到#34;以及#34;时间戳)并按设备对消息进行分组,因此我们每个设备只能获得一条消息。 Orderby descending和FirstOrDefault - 用于获得最高的一个。时间戳上有降序索引。

在循环中运行它的问题是什么?好吧,用户希望用新的数据(最后一天,间隔:1分钟)快速查询很长一段时间,这意味着使用不同的参数(时间戳)重新查询同一查询的数量非常大。对于有限数量的数据,它每秒查询数十次时间戳,并且我认为查询将使用时间戳列表运行一次以便更快地完成。

有没有办法连接同一个查询的多个运行,一次可以获得更大批量的数据?我使用的是EF5但我并不害怕编写存储过程和sql函数。

1 个答案:

答案 0 :(得分:2)

除了IntervalID之外,我还会尝试通过某种“DeviceID”扩展分组。 IntervalID将是一个整数,用于表示句点开始和结束之间的间隔。您可以为查询输入三个参数:

DateTime periodStart = ...;
DateTime periodEnd = ...;
int interval = ...; // unit = seconds for example, for 5 minutes it's 300, etc.

评估期内给定消息的IntervalID是在查询期间“动态”计算的属性,在.NET中它将是:

IntervalID = Math.Floor((msg.Timestamp - periodStart).TotalSeconds / interval);

您很可能无法在LINQ-to-Entities查询中直接使用此表达式,因为可能不支持两个DateTime值的减运算符。 (LINQ-to-Entities支持Math.Floor。)但您可以尝试用EntityFunctions(或DbFunctions替换EF&gt; = 6)替换它。整个查询可能如下所示:

DateTime periodStart = ...;
DateTime periodEnd = ...;
int interval = ...; // unit = seconds for example, for 5 minutes it's 300, etc.

var query = from message in this.Messages
            where  message.Timestamp >= periodStart
                && message.Timestamp <= periodEnd
                && message.Device.Type == 254
            select new
            {
                Message = message,
                IntervalID = Math.Floor(EntityFunctions.DiffSeconds(
                    message.TimeStamp, periodStart) / interval)
            } into messageInInterval
            group messageInInterval by new
            {
                DeviceID = messageInInterval.Message.DeviceID,
                IntervalID = messageInInterval.IntervalID
            } into g
            select (from messageInInterval in g
                    orderby messageInInterval.Message.Timestamp descending
                    select messageInInterval.Message).FirstOrDefault();

query.ToList()提供一个消息列表,其中包含每个设备和每个时间间隔的最后一条消息。

(只是一个基本想法,代码未经测试。)