分层数据的高级 LINQ 分组和投影查询(EF 4.0 + LINQ + ASP.NET MVC + HighChart

本文关键字:LINQ EF HighChart ASP NET MVC 查询 高级 数据 投影 分层 | 更新日期: 2023-09-27 18:36:07

>问题概述:

我有数据库表,用于描述使用 Web 服务从多个设备收集到集中式数据库中的数据收集。

存储结果的表为了提高性能而故意非规范化:更快的查询和按多个索引分组。我正在使用实体框架和Linq进行数据访问。

我需要正确设计具有分层分组和 投影


设备数据库建模概述:

目前我有 2 种类型的设备

1. 射频识别设备

第一个表是描述收集数据的标签的 RfidTag,1 个单个 RfidTag = 1 个单个传感器。因此,例如1个标签可以获取有关温度的数据。

第二个表是RfidReader,它描述了读取模块,该模块收集所有数据并将其从附加标签发送到它。对单个 RfidReader 附加的 RfidTag 的数量没有限制。但是,在读取期间,可以将单个 RfidTag 附加到单个 RfidReader。

CREATE TABLE [dbo].[RfidTag]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    ReaderId INT NULL,                  -- Reference to reader
    SensorTypeId INT NOT NULL,          -- Reference to sensor type
    SensorParameters NVARCHAR(50) NULL, -- Sensor parameters
    Hex  NVARCHAR(50) NOT NULL,         -- Hex tag identifier stored as string
    Name NVARCHAR(50) NOT NULL,         -- Tag name
    [Description] NVARCHAR(200) NULL,   -- Tag description
    --
)
CREATE TABLE [dbo].[RfidReader]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime  DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    Name        NVARCHAR(20),        -- Tag name
    [Description]   NVARCHAR(200),   -- Tag description                                             
    SerialNumber    NVARCHAR(12),    -- Unique device serial name               
    --
)

每个RfidReader都可以连接到针对特定结构描述的特定测量区域

CREATE TABLE [dbo].[RfidReaderPlacement]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    --
    ReaderId INT NOT NULL,              -- Reference to Reader.
    MeasurementZoneId INT NOT NULL,     -- Reference to Measurement Zone.
    StartDate DATETIME NOT NULL,        -- Start date of reading.
    StopDate  DATETIME,                 -- End date of reading.
    --
)

RfidTag 收集的单个数据保存在非规范化表中。此表存储数百万条记录,负载非常繁重。在此表中,我们将使用 LINQ 查询收集数据。

CREATE TABLE [dbo].[RfidReading]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    ReaderPlacementId INT NOT NULL, -- Reference to Rfid Reader Placement
    ConstructionId  INT NOT NULL,   -- Reference to Construction
    MeasurementZoneId INT NOT NULL, -- Reference to Measurement Zone
    ReaderId INT NOT NULL,          -- Reference to Rfid Reader
    TagId    INT NOT NULL,          -- Reference to Rfid Tag
    SensorTypeId INT NOT NULL,      -- Reference to Sensor Type
    ReadingDate DATETIME NOT NULL,  -- Reading date
    Value FLOAT NOT NULL            -- Measured value
    --
)

2. ZigBee设备

第一个表是ZigBeeNodeProbe,它描述了收集数据的单个探针,1个ZigBeeNodeProbe = 1个单个传感器。例如,1个探头可以获取有关温度的数据。

第二个表是ZigBeeNode,它描述了包含附加探针的单个设备,1个ZigBeeNode = 3个ZigBeeNodeProbes

第三个表是ZigBeeReader,它描述了读取模块,该模块收集所有数据并将其从连接的节点发送到它。对单个ZigBeeReader的附加ZigBeeNode(已连接ZigBeeNodeProbes)的数量没有限制。但是,在读取期间,可以将单个ZigBeeNode连接到单个ZigBeeReader。

CREATE TABLE [dbo].[ZigBeeNodeProbe]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    --
    NodeId INT NULL,                    -- Reference to node
    SensorTypeId INT NOT NULL,          -- Reference to sensor type
    SensorParameters NVARCHAR(50) NULL, -- Sensor parameters
    SocketNumber  INT NOT NULL,         -- Socket number used in parent ZigBeeNode
    Name NVARCHAR(50) NOT NULL,         -- Node name
    [Description] NVARCHAR(200) NULL,   -- Node description
    --
)
CREATE TABLE [dbo].[ZigBeeNode]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    ReaderId INT NULL,                    -- Reference to reader
    NetworkAddress  NVARCHAR(50) NOT NULL,-- Node address in ZigBee network
    Name NVARCHAR(50) NOT NULL,           -- Given name
    [Description] NVARCHAR(200) NULL,     -- Tag description
    SocketCount INT NOT NULL DEFAULT 0,   -- Count of available sockets to plug in probe
    NodeFrequency INT NULL,               -- Node frequency
)
CREATE TABLE [dbo].[ZigBeeReader]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime  DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    Name        NVARCHAR(20),        -- Tag name
    [Description]   NVARCHAR(200),   -- Tag description                                             
    SerialNumber    NVARCHAR(12),    -- Unique device serial name               
    --
)

每个ZigBeeReader都可以连接到针对特定结构描述的特定测量区域

CREATE TABLE [dbo].[ZigBeeReaderPlacement]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    --
    ReaderId INT NOT NULL,              -- Reference to Reader.
    MeasurementZoneId INT NOT NULL,     -- Reference to Measurement Zone.
    StartDate DATETIME NOT NULL,        -- Start date of reading.
    StopDate  DATETIME,                 -- End date of reading.
    --
)

ZigBeeNodeProbe收集的单个数据保存在非规范化表中。此表存储数百万条记录,负载非常繁重。在此表中,我们将使用 LINQ 查询收集数据。

CREATE TABLE [dbo].[ZigBeeReading]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    ReaderPlacementId INT NOT NULL, -- Reference to ZigBee Reader Placement
    ConstructionId    INT NOT NULL, -- Reference to Construction
    MeasurementZoneId INT NOT NULL, -- Reference to Measurement Zone
    ReaderId      INT NOT NULL,     -- Reference to ZigBee Reader
    NodeId        INT NOT NULL,     -- Reference to ZigBee Node
    ProbeId       INT NOT NULL,     -- Reference to ZigBee Node Probe
    SensorTypeId      INT NOT NULL, -- Reference to Sensor Type
    ReadingDate   DATETIME NOT NULL,-- Reading date
    Value         FLOAT NOT NULL    -- Measured value
    --
)

查询、分组和投影问题:

正如您在上面看到的,我们有 2 个去中心化表,其中包含由 2 种提到的设备收集的数据。是的,我们可以假设RfidTag在业务建模中与ZigBeeNodeProbe几乎相同。

RfidReader
-- RFidTag
-- RFidTag
...
ZigBeeReader
-- ZigBeeNode
---- ZigBeeNodeProbe
---- ZigBeeNodeProbe
---- ZigBeeNodeProbe
-- ZigBeeNode
---- ZigBeeNodeProbe
---- ZigBeeNodeProbe
---- ZigBeeNodeProbe
...

现在我们必须查询这两个表,将不同表中的值投影到相同的视图模型中,并添加特定的分组来过滤数据。

常见场景:

我们想要创建显示特定测量区域平均温度的图表,请记住,特定测量区域可能包含多个附加的读取器(Rfid 和 ZigBee),我们必须提供一系列数据。

我正在使用必须已启用的HightStock http://www.highcharts.com/products/highstock 图表:

  • 缩放 1 个月、3 个月、6 个月等。
  • "开始"、"结束"期间
  • 导出要素
  • 带有启用和禁用系列的图例

图表示例:

http://jsfiddle.net/hNHUY/1/

问题:

如何为分层数据创建 LINQ 分组和投影查询?我们需要为 RfidReading 和 ZigBeeReading 表提供通用视图模型。

我的第一次尝试是这样:

public class ReadingReaderDataModel
{
    public string SeriesName { get; set; }
    public ReadingPeriod ReadingPeriod { get; set; }
    public IEnumerable<ReadingNodeDataModel> ReadingNodeDataModels { get; set; }
    public ReadingReaderDataModel()
    {
        ReadingNodeDataModels = new List<ReadingNodeDataModel>();
    }
}
public class ReadingNodeDataModel
{
    public string NodeName { get; set; }
    public IEnumerable<double> DataValues { get; set; }
    public ReadingNodeDataModel()
    {
        DataValues = new LinkedList<double>();
    }
}
public enum ReadingPeriod
{
    OneMonth, ThreeMonths, SixMonths, YearToDay, OneYear, All
}
public enum ReaderType
{
    Rfid, ZigBee
}

稍后,我必须为 ASP.NET MVC 控制器设计 LINQ 投影,在这里我不知道如何创建正确的查询,该查询必须包含按一个月、三个月、六个月、年到日、一年、全部分组的时间段的平均值。

谁能帮我设计这个高级 LINQ 查询?

编辑

请不要向我指出任何针对大型数据集的"建议"......

这不是关于性能的问题。

此问题是关于复杂的 LINQ 查询的。我正在寻找正确的 LINQ 代码,而不是像"那辆车更快,你应该尝试使用那辆车"这样的答案......请代码答案...如果您甚至不尝试理解场景并提供任何代码,请不要参与...

我特别添加了赏金来查找 LINQ 的解决方案,因为它是高级 LINQ 问题,就像这个问题的主题一样。

分层数据的高级 LINQ 分组和投影查询(EF 4.0 + LINQ + ASP.NET MVC + HighChart

据我了解,数据库中有数百万条记录,并且您的应用程序需要非常好的性能,并且您的结构非常复杂。

远离EF,我是认真的。您可以将其用于较小的数据集,但对于任何需要从大型查询中获得卓越性能的东西,甚至不要去那里。

我的建议是手动编写查询,因为如果您想使用 EF 尽可能多地进行优化,您实际上无论如何都无法跳过太多代码,并且基本上将以 LINQ 方式编写 SQL(即指定所有 SELECT 语句)。

如果你想要更具体一点,请使用 Dapper。它是运行StackOverflow的"ORM"。这里有关于速度的比较:http://code.google.com/p/dapper-dot-net/

但是,如果您死心塌地地使用 EF...

不要尝试编写一个查询。它不会发生。相反,将它们分开。为您需要制作的每个组选择一个不同的。这是迄今为止最简单的出路。我相信您应该已经有一个 LINQ 查询来只获取一个组。现在对你需要的所有其他组重复这一点,特别是如果你有大量的Wheres和OrderBy。

来源:一个每天处理大型数据集查询的人。