对传感器/读取/警报数据库设计的意见

发布于 2024-10-06 07:55:22 字数 1465 浏览 3 评论 0原文

我最近问了一些关于数据库设计的问题,可能太多了;-)但是我相信我正在慢慢地通过我的设计找到问题的核心,并慢慢地将其简化。我仍在努力思考如何将“警报”存储在数据库中。

在这个系统中,警报是一个必须被确认、采取行动等的实体。

最初,我将读数与警报相关联,如下所示(非常精简): -

[Location]
LocationId

[Sensor]
SensorId
LocationId
UpperLimitValue
LowerLimitValue

[SensorReading]
SensorReadingId
Value
Status
Timestamp

[SensorAlert]
SensorAlertId

[SensorAlertReading]
SensorAlertId
SensorReadingId

最后一个表将读数与警报相关联,因为它是读数指示传感器是否处于警戒状态。

这种设计的问题在于,它允许来自多个传感器的读数与单个警报相关联 - 而每个警报仅针对单个传感器,并且应该仅具有与其关联的该传感器的读数(我应该担心数据库允许不过这个?)。

我想简化事情,为什么还要费心使用 SensorAlertReading 表呢?相反,我可以这样做:

[Location]
LocationId

[Sensor]
SensorId
LocationId

[SensorReading]
SensorReadingId
SensorId
Value
Status
Timestamp

[SensorAlert]
SensorAlertId
SensorId
Timestamp

[SensorAlertEnd]
SensorAlertId
Timestamp

基本上我现在不会将读数与警报关联起来 - 相反,我只知道警报在特定传感器的开始时间和结束时间之间处于活动状态,并且如果我想查找该警报的读数我能做到。

显然,缺点是我不再有任何限制阻止我删除警报期间发生的读数,但我不确定该限制是否必要。

现在从外部来看,作为一名开发人员/DBA,这会让您感到恶心还是看起来合理?

也许还有我可能缺少的另一种方法吗?

谢谢。

编辑: 这是另一个想法——它以不同的方式运作。它将每个传感器状态变化(从正常到警报)存储在表中,然后将读数简单地与特定状态相关联。这似乎解决了所有问题 - 你觉得怎么样? (我唯一不确定的是将该表称为“SensorState”,我不禁想到有一个更好的名称(也许是 SensorReadingGroup?): -

[Location]
LocationId

[Sensor]
SensorId
LocationId

[SensorState]
SensorStateId
SensorId
Timestamp
Status
IsInAlert

[SensorReading]
SensorReadingId
SensorStateId
Value
Timestamp

必须有一个优雅的解决方案!

I've asked a few questions lately regarding database design, probably too many ;-) However I beleive I'm slowly getting to the heart of the matter with my design and am slowly boiling it down. I'm still wrestling with a couple of decisions regarding how "alerts" are stored in the database.

In this system, an alert is an entity that must be acknowledged, acted upon, etc.

Initially I related readings to alerts like this (very cut down) : -

[Location]
LocationId

[Sensor]
SensorId
LocationId
UpperLimitValue
LowerLimitValue

[SensorReading]
SensorReadingId
Value
Status
Timestamp

[SensorAlert]
SensorAlertId

[SensorAlertReading]
SensorAlertId
SensorReadingId

The last table is associating readings with the alert, because it is the reading that dictate that the sensor is in alert or not.

The problem with this design is that it allows readings from many sensors to be associated with a single alert - whereas each alert is for a single sensor only and should only have readings for that sensor associated with it (should I be bothered that the DB allows this though?).

I thought to simplify things, why even bother with the SensorAlertReading table? Instead I could do this:

[Location]
LocationId

[Sensor]
SensorId
LocationId

[SensorReading]
SensorReadingId
SensorId
Value
Status
Timestamp

[SensorAlert]
SensorAlertId
SensorId
Timestamp

[SensorAlertEnd]
SensorAlertId
Timestamp

Basically I'm not associating readings with the alert now - instead I just know that an alert was active between a start and end time for a particular sensor, and if I want to look up the readings for that alert I can do.

Obviously the downside is I no longer have any constraint stopping me deleting readings that occurred during the alert, but I'm not sure that the constraint is neccessary.

Now looking in from the outside as a developer / DBA, would that make you want to be sick or does it seem reasonable?

Is there perhaps another way of doing this that I may be missing?

Thanks.

EDIT:
Here's another idea - it works in a different way. It stores each sensor state change, going from normal to alert in a table, and then readings are simply associated with a particular state. This seems to solve all the problems - what d'ya think? (the only thing I'm not sure about is calling the table "SensorState", I can't help think there's a better name (maybe SensorReadingGroup?) : -

[Location]
LocationId

[Sensor]
SensorId
LocationId

[SensorState]
SensorStateId
SensorId
Timestamp
Status
IsInAlert

[SensorReading]
SensorReadingId
SensorStateId
Value
Timestamp

There must be an elegant solution to this!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不可一世的女人 2024-10-13 07:55:22

修订于 1 月 11 日 21:50 UTC

数据模型

我认为您的数据模型应该如下所示:▶传感器数据模型◀。 (第 2 页与您有关历史的其他问题相关)。

不熟悉关系建模标准的读者可能会发现▶ IDEF1X 表示法◀很有用。

业务(在评论中制定的规则)

我确实发现了一些早期的业务规则,这些规则现在已经过时了,所以我已经删除了它们

这些可以在关系中“阅读”(请阅读数据模型旁边的内容)。业务规则和所有隐含的引用和数据完整性都可以在任何 ISO SQL 数据库中的 RULES、CHECK 约束中实现,并由此得到保证。这是 IDEF1X 在关系键以及实体和关系的开发中的演示。请注意,动词短语不仅仅是华丽的。

除了三个参考表之外,唯一的静态识别实体是位置、网络从站和用户。传感器是系统的核心,所以我给了它自己的标题。

位置

  • 一个位置包含一对多传感器
  • 一个位置可能有一个记录器

NetworkSlave

  • 收集一对多网络传感器的读数

用户

  • 一个用户可以维护零到多个位置
  • 一个用户 可以维护零到多个传感器
  • 一个用户 可以维护零到多个NetworkSlaves
  • 一个用户 code> 可以执行零到多的下载
  • 一个用户可以进行零到多的确认,每个警报< /code>
  • 一个用户可以采取零到多个Actions,每一个ActionType

Sensor

  • SensorType 安装为零到多 传感器

  • 一个记录器(容纳并)收集一个LoggerSensor读数

  • A Sensor其中一个一个NetworkSensor 一个 LoggerSensor

    • NetworkSensor 记录一个 NetworkSlave 收集的读数
  • Logger 定期一对多下载
    • LoggerSensor 记录一个 Logger 收集的读数
  • 一个Reading可以被认为是一个AlertTypeAlert
    • AlertType 可能会发生在零到多读数
  • 一个警报可能是一个用户的一个确认
  • 确认可以由一个Action、一个ActionType、一个User关闭
    • 一个 ActionType 可以用于零到多个 Actions

对评论的响应

  1. 粘贴 Id 列上所有移动的东西都会干扰标识符的确定,标识符是赋予数据库关系“能力”的自然关系键。它们是代理键,这意味着附加键和索引,它阻碍了关系的力量;这会导致比其他必要的连接更多的连接。因此,只有当关系键变得太麻烦而无法迁移到子表(并接受强加的额外联接)时,我才使用它们。

  2. 可空键是非规范化数据库的典型症状。数据库中的空值对于性能来说是个坏消息;但是 FK 中的 Null 意味着每个表做了太多的事情,有太多的含义,并且结果是非常糟糕的代码。适合喜欢“重构”数据库的人;对于关系数据库来说完全没有必要。

  3. 已解决:警报可能已已确认确认可能会已采取行动

  4. 该行上方的列是主键(请参阅符号文档)。 SensorNoLocationId 内的连续编号;参考业务规则,在 Location 之外没有任何意义;两列共同构成PK。当您准备插入传感器时(在检查尝试是否有效等之后),它将按如下方式导出。这不包括 LoggerSensors,其为零:

    INSERT Sensor VALUES (
        @LocationId,
        传感器编号 = ( SELECT ISNULL(MAX(传感器编号), 0) + 1
            来自传感器
            WHERE 位置 ID = @LocationId
            )
        @SensorCode
        )

  5. 为了准确性或改进的含义,我已将 NetworkSlave 监视 NetworkSensor 更改为 NetworkSlave 从 NetworkSensor 收集读数

  6. 检查约束。 NetworkSensorLoggerSensorSensor 的专有子类型,它们的完整性可以通过 CHECK 约束设置。 Alerts、AcknowledgementsActions 不是子类型,但它们的完整性是通过相同的方法设置的,所以我将它们一起列出。

    • 数据模型中的每个关系都作为子类型(或子类型)中的约束实现为 FOREIGN KEY (child_FK_columns) REFERENCES Parent (PK_columns)

    • 需要鉴别器来识别传感器属于哪种子类型。这是 LoggerSensorsSensorNo = 0;对于 NetworkSensors 来说非零。

    • NetworkSensorsLoggerSensors 的存在分别受到 NetworkSlaveLogger 的 FK CONSTRAINTS 约束;以及传感器。
    • NetworkSensor 中,包含 CHECK 约束以确保 SensorNo 非零
    • LoggerSensor 中,包含 CHECK 约束以确保 SensorNo 为零

    • AcknowledgementsActions 的存在受到已识别的 FK CONSTRAINTS 的约束(如果没有 Acknowledgement 就不可能存在)警报;没有确认就不可能存在操作)。相反,没有 AcknowledgementAlert 处于未确认状态;带有确认但没有操作警报处于已确认但未操作状态。

  7. 警报。这种(实时监控和警报)应用程序的设计概念是许多独立运行的小程序;所有这些都使用数据库作为事实的单一版本。有些程序插入行(读数、警报);其他程序轮询数据库是否存在此类行(并发送 SMS 消息等;或者手持设备仅拾取与该设备相关的警报)。从这个意义上说,数据库可以被描述为一个消息框(一个程序将行放入其中,另一个程序读取并执行操作)。

    假设是,传感器读数NetworkSlave“实时”记录,并且每分钟左右记录一组新数据已插入 Readings。后台进程定期执行(每分钟或其他),这是主要的“监视”程序,它将在其循环中具有许多功能。其中一个功能是监视读数并生成自上次迭代(程序循环)以来发生的警报。

    以下代码段将在循环内执行,每个警报类型对应一个。这是一个经典的投影:

    --假设@LoopDateTime包含最后一次迭代的DateTime
    插入警报
        选择位置 ID,
               传感器没有,
               读Dtm,
               “L”——警报类型“低”
            来自传感器,
                 阅读 r
            WHERE s.LocationId = r.LocationId
            AND s.SensorNo = r.SensorNo
            AND r.ReadingDtm > @LoopDtm
            AND r.Value < s.下限
    插入警报
        选择位置 ID,
               传感器没有,
               读Dtm,
               “H”——警报类型“高”
            来自传感器,
                 阅读 r
            WHERE s.LocationId = r.LocationId
            AND s.SensorNo = r.SensorNo
            AND r.ReadingDtm > @LoopDtm
            AND r.值> s.UpperLimit

    因此,Alert 绝对是一个事实,它作为数据库中的一行存在。随后,可能会被用户(另一行/事实)确认,并由ActionType通过ActionType进行操作用户

    除此之外(通过投影行为进行创作),即。在一般且不变的情况下,我只会将 Alert 引用为 Alert 中的一行;创建后的静态对象。

  8. 关于更改用户的担忧。这已经得到解决,如下所示。在我(昨天修改的)答案的顶部,我声明主要的识别元素是静态。我对业务规则进行了重新排序以提高清晰度。

    • 由于您提到的原因,User.Name 对于 User 来说并不是一个好的 PK,尽管它仍然是一个备用密钥(唯一)并且用于人际交互。

    • User.Name不能重复,不能有多个Fred;可以用名字-姓氏表示;两个Fred Bloggs,但不是User.Name。我们的第二个 Fred 需要选择另一个 User.Name。记下已确定的指数。

    • UserId是永久记录,已经是PK了。千万不要删除User,它具有历史意义。事实上,FK 约束会阻止你(永远不要在真正的数据库中使用 CASCADE,那纯粹是疯狂的)。不需要代码或触发器等。

    • 或者(删除从未执行过任何操作的Users,从而释放User.Name以供使用)只要不存在 FK 违规(即UserId下载、确认、操作被引用。

    要确保只有当前的用户执行操作,请在 User (DM Updated) 中添加 IsObsolete 布尔值,并检查该列当该表被询问任何函数(报告除外)时,您可以实现一个 View UserCurrent ,它仅返回那些 Users

    LocationNetworkSlave 也是如此。如果您需要区分当前与历史,请告诉我,我也会向它们添加 IsObsolete

    我不知道:您可以定期清除数据库中的古代历史数据,删除(例如)超过 10 年的行。这必须首先从底部(表格)开始,建立关系。

随意提问。

请注意,IDEF1 表示法文档已被扩展。

Revised 01 Jan 11 21:50 UTC

Data Model

I think your Data Model should look like this:▶Sensor Data Model◀. (Page 2 relates to your other question re History).

Readers who are unfamiliar with the Relational Modelling Standard may find ▶IDEF1X Notation◀ useful.

Business (Rules Developed in the Commentary)

I did identify some early business Rules, which are now obsolete, so I have deleted them

These can be "read" in the Relations (read adjacent to the Data Model). The Business Rules and all implied Referential and Data Integrity can be implemented in, and thus guaranteed by, RULES, CHECK Constraints, in any ISO SQL database. This is a demonstration of IDEF1X, in the development of both the Relational keys, and the Entities and Relations. Note the Verb Phrases are more than mere flourish.

Apart from three Reference tables, the only static, Identifying entities are Location, NetworkSlave, and User. Sensor is central to the system, so I ahve given it its own heading.

Location

  • A Location contains one-to-many Sensors
  • A Location may have one Logger

NetworkSlave

  • A NetworkSlave collects Readings for one-to-many NetworkSensors

User

  • An User may maintain zero-to-many Locations
  • An User may maintain zero-to-many Sensors
  • An User may maintain zero-to-many NetworkSlaves
  • An User may perform zero-to-many Downloads
  • An User may make zero-to-many Acknowledgements, each on one Alert
  • An User may take zero-to-many Actions, each of one ActionType

Sensor

  • A SensorType is installed as zero-to-many Sensors

  • A Logger (houses and) collects Readings for one LoggerSensor

  • A Sensor is either one NetworkSensor or one LoggerSensor

    • A NetworkSensor records Readings collected by one NetworkSlave
      .
  • A Logger is periodically Downloaded one-to-many times
    • A LoggerSensor records Readings collected by one Logger
      .
  • A Reading may be deemed in Alert, of one AlertType
    • An AlertType may happen on zero-to-many Readings
      .
  • An Alert may be one Acknowledgement, by one User
    .
  • An Acknowledgement may be closed by one Action, of one ActionType, by one User
    • An ActionType may be taken on zero-to-many Actions

Responses to Comments

  1. Sticking Id columns on everything that moves, interferes with the determination of Identifiers, the natural Relational keys that give your database relational "power". They are Surrogate Keys, which means an additional Key and Index, and it hinders that relational power; which results in more joins than otherwise necessary. Therefore I use them only when the Relational key becomes too cumbersome to migrate to the child tables (and accept the imposed extra join).

  2. Nullable keys are a classic symptom of an Unnormalised database. Nulls in the database is bad news for performance; but Nulls in FKs means each table is doing too many things, has too many meanings, and results is very poor code. Good for people who like to "refactor" their databases; completely unnecessary for a Relational database.

  3. Resolved: An Alert may be Acknowledged; An Acknowledgement may be Actioned.

  4. The columns above the line are the Primary Key (refer Notation document). SensorNo is a sequential number within LocationId; refer Business Rules, it is meaningless outside a Location; the two columns together form the PK. When you are ready to INSERT a Sensor (after you have checked that the attempt is valid, etc), it is derived as follows. This excludes LoggerSensors, which are zero:

    INSERT Sensor VALUES (
        @LocationId,
        SensorNo = ( SELECT ISNULL(MAX(SensorNo), 0) + 1
            FROM Sensor
            WHERE LocationId = @LocationId
            )
        @SensorCode
        )

  5. For accuracy or improved meaning, I have changed NetworkSlave monitors NetworkSensor to NetworkSlave collects Readings from NetworkSensor.

  6. Check Constraints. The NetworkSensor and LoggerSensor are exclusive subtypes of Sensor, and their integrity can be set by CHECK constraints. Alerts, Acknowledgements and Actions are not subtypes, but their integrity is set by the same method, so I will list them together.

    • Every Relation in the Data Model is implemented as a CONSTRAINT in the child (or subtype) as FOREIGN KEY (child_FK_columns) REFERENCES Parent (PK_columns)

    • A Discriminator is required to identify which subtype a Sensor is. This is SensorNo = 0 for LoggerSensors; and non-zero for NetworkSensors.

    • The existence of NetworkSensors and LoggerSensors are constrained by the FK CONSTRAINTS to NetworkSlave and Logger, respectively; as well as to Sensor.
    • In NetworkSensor, include a CHECK constraint to ensure SensorNo is non-zero
    • In LoggerSensor, include a CHECK constraint to ensure SensorNo is zero

    • The existence of Acknowledgements and Actions are constrained by the identified FK CONSTRAINTS (An Acknowledgement cannot exist without an Alert; an Action cannot exist without an Acknowledgement). Conversely, an Alert with no Acknowledgement is in an unacknowledged state; an Alert with and Acknowledgementbut no Action is in an acknowledged but un-actioned state.
      .

  7. Alerts. The concept in a design for this kind of (live monitoring and alert) application is many small programs, running independently; all using the database as the single version of the truth. Some programs insert rows (Readings, Alerts); other programs poll the db for existence of such rows (and send SMS messages, etc; or hand-held units pick up Alerts relevant to the unit only). In that sense, the db is a may be described as an message box (one program puts rows in, which another program reads and actions).

    The assumption is, Readings for Sensors are being recorded "live" by the NetworkSlave, and every minute or so, a new set of Readings is inserted. A background process executes periodically (every minute or whatever), this is the main "monitor" program, it will have many functions within its loop. One such function will be to monitor Readings and produce Alerts that have occurred since the last iteration (of the program loop).

    The following code segment will be executed within the loop, one for each AlertType. It is a classic Projection:

    -- Assume @LoopDateTime contains the DateTime of the last iteration
    INSERT Alert
        SELECT LocationId,
               SensorNo,
               ReadingDtm,
               "L"          -- AlertType "Low"
            FROM Sensor  s,
                 Reading r
            WHERE s.LocationId = r.LocationId
            AND   s.SensorNo   = r.SensorNo
            AND   r.ReadingDtm > @LoopDtm
            AND   r.Value      < s.LowerLimit
    INSERT Alert
        SELECT LocationId,
               SensorNo,
               ReadingDtm,
               "H"          -- AlertType "High"
            FROM Sensor  s,
                 Reading r
            WHERE s.LocationId = r.LocationId
            AND   s.SensorNo   = r.SensorNo
            AND   r.ReadingDtm > @LoopDtm
            AND   r.Value      > s.UpperLimit

    So an Alert is definitely a fact, that exists as a row in the database. Subsequently that may be Acknowledged by an User (another row/fact), and Actioned with an ActionType by an User.

    Other that this (the creation by Projection act), ie. the general and unvarying case, I would refer to Alert only as a row in Alert; a static object after creation.

  8. Concerns re Changing Users. That is taken care of already, as follows. At the top of my (revised yesterday) Answer, I state that the major Identifying elements are static. I have re-sequenced the Business Rules to improve clarity.

    • For the reasons you mention, User.Name is not a good PK for User, although it remains an Alternate Key (Unique) and the one that is used for human interaction.

    • User.Name cannot be duplicated, there cannot be more than one Fred; there can be in terms of FirstName-LastName; two Fred Bloggs, but not in terms of User.Name. Our second Fred needs to choose another User.Name. Note the identified Indices.

    • UserId is the permanent record, and it is already the PK. Never delete User, it has historical significance. In fact the FK constraints will stop you (never use CASCADE in a real database, that is pure insanity). No need for code or triggers, etc.

    • Alternately (to delete Users who never did anything, and thus release User.Name for use) allow Delete as long as there are no FK violations (ie. UserId is not referenced in Download, Acknowledgement, Action).

    To ensure that only Users who are Current perform Actions, add an IsObsolete boolean in User (DM Updated), and check that column when that table is interrogated for any function (except reports) You can implement a View UserCurrent which returns only those Users.

    Same goes for Location and NetworkSlave. If you need to differentiate current vs historical, let me know, I will add IsObsolete to them as well.

    I don't know: you may purge the database of ancient Historical data periodically, delete rows that are (eg) over 10 years old. That has to be done from the bottom (tables) first, working up the Relations.

Feel free to ask Questions.

Note the IDEF1 Notation document has been expanded.

青春有你 2024-10-13 07:55:22

这是我对这个问题的两点看法。

alt text

AlertType 表包含所有可能的警报类型。 AlertName 可能是高温、低压、低水位等。

AlertSetup 表允许为特定警报类型设置来自传感器的警报阈值。
例如,TresholdLevel = 100 和 TresholdType = 'HI' 应在读数超过 100 时触发警报。

读数表保存流式传输的传感器读数进入服务器(应用程序)。

警报表保存所有警报。它保留触发警报的第一个读数和完成警报的最后一个读数的链接(FirstReadingIdLastReadingId)。如果 (SensorId, AlertTypeId) 组合存在活动警报,则 IsActive 为 true。仅当读取低于警报阈值时,IsActive 才能设置为 false。 IsAcknowledged 表示操作员已确认警报。

  1. 应用层将新的读数插入到Reading表中,捕获ReadingId

  2. 然后,应用程序根据每个(SensorIdAlertTypeId)组合的警报设置检查读数。此时,将创建对象集合 {SensorId, AlertTypeId, ReadingId, IsAlert},并为每个对象设置 IsAlert 标志。

  3. 然后检查Alert表中集合中每个对象{SensorId, AlertTypeId, ReadingId, IsAlert}的活动警报。

    • 如果 IsAlert 为 TRUE,并且 (SensorId, AlertTypeId) 组合没有活动警报,则创建一个新行添加到 Alert 表中,其中 FirstReadingID 指向当前 ReadingIdIsActive 设置为 TRUE,IsAcknowledged 设置为 FALSE。

    • 如果 IsAlert 为 TRUE,并且 (SensorId, AlertTypeId) 组合存在活动警报,则该行为通过设置指向当前 ReadingIdLastReadingID 进行更新。

    • 如果 IsAlert 为 FALSE,并且 (SensorId, AlertTypeId) 组合存在活动警报,则该行为通过设置 IsActive FALSE 进行更新。

    • 如果 IsAlert 为 FALSE,并且 (SensorId, AlertTypeId) 组合没有活动警报,则 警报表未修改。

Here are my two cents on the problem.

alt text

AlertType table holds all possible types of alerts. AlertName may be something like high temperate, low pressure, low water level, etc.

AlertSetup table allows for setup of alert thresholds from a sensor for a specific alert type.
For example, TresholdLevel = 100 and TresholdType = 'HI' should trigger alert for readings over 100.

Reading table holds sensor readings as they are streamed into the server (application).

Alert table holds all alerts. It keeps links to the first reading that triggered the alert and the last one that finished it (FirstReadingId, LastReadingId). IsActive is true if there is an active alert for the (SensorId, AlertTypeId) combination. IsActive can be set to false only by reading going below the alert threshold. IsAcknowledged means that an operator has acknowledged the alert.

  1. The application layer inserts the new reading into the Reading table, captures the ReadingId.

  2. Then application checks the reading against alert setups for each (SensorId, AlertTypeId) combination. At this point a collection of objects {SensorId, AlertTypeId, ReadingId, IsAlert} is created and the IsAlert flag is set for each object.

  3. The Alert table is then checked for active alerts for each object {SensorId, AlertTypeId, ReadingId, IsAlert} from the collection.

    • If the IsAlert is TRUE and there are no active alerts for the (SensorId, AlertTypeId) combination, a new row is added to the Alert table with the FirstReadingID pointing to the current ReadingId. The IsActive is set to TRUE, the IsAcknowledged to FALSE.

    • If the IsAlert is TRUE and there is an active alert for the (SensorId, AlertTypeId) combination, that row is updated by setting the LastReadingID pointing to the current ReadingId.

    • If the IsAlert is FALSE and there is an active alert for the (SensorId, AlertTypeId) combination, that row is updated by setting the IsActive FALSE.

    • If the IsAlert is FALSE and there are no active alerts for the (SensorId, AlertTypeId) combination, the Alert table is not modified.

相思故 2024-10-13 07:55:22

这里您必须处理的主要“三角”是传感器、[传感器]读取和警报。假设您必须跟踪正在发生的活动(而不是“一次加载全部”设计),您的第三个解决方案与我们最近所做的类似。经过一些调整,它看起来像:

[Location] 
LocationId 

[Sensor] 
SensorId 
LocationId 
CurrentSensorState  --  Denormalized data!

[SensorReading] 
SensorReadingId 
SensorState
Value 
Timestamp 

[SensorStateLog] 
SensorId 
Timestamp 
SensorState
Status   --  Does what?
IsInAlert 
(Primary key is {SensorId, Timestamp})

“SensorState”可以是 SensorStateId,并有一个关联的查找表列出(并限制)所有可能的状态。

这个想法是,您的传感器每个传感器包含一行并显示其当前状态。 SensorReading 随传感器读数不断更新。如果给定传感器的当前状态发生变化(即新读数的状态与传感器的当前状态不同),您可以更改当前状态并向 SensorStateLog 添加一行以显示状态的变化。 (或者,您可以使用“状态结束”时间戳更新该传感器的“先前”条目,但这是要编写的繁琐代码。)

传感器表中的 CurrentSensorState 是非规范化数据,但如果维护得当(并且如果您有数百万个传感器)行)它将大大提高查询当前状态的效率,因此可能值得付出努力。

所有这一切的明显缺点是警报不再是一个实体,并且它们变得更难以跟踪和识别。如果这些必须易于立即识别和使用,那么您的第三个方案将无法满足您的需要。

The main "triangle" you have to deal with here is Sensor, [Sensor]Reading, and Alert. Presuming you have to track activity as it is occuring (as opposed to a "load it all at once" design), your third solution is similar to something we did recently. A few tweaks and it would look like:

[Location] 
LocationId 

[Sensor] 
SensorId 
LocationId 
CurrentSensorState  --  Denormalized data!

[SensorReading] 
SensorReadingId 
SensorState
Value 
Timestamp 

[SensorStateLog] 
SensorId 
Timestamp 
SensorState
Status   --  Does what?
IsInAlert 
(Primary key is {SensorId, Timestamp})

"SensorState" could be SensorStateId, with an associated lookup table listing (and constraining) all possible states.

The idea is, you Sensor contains one row per sensor and shows it's current state. SensorReading is updated continuously with sensor readings. If and when a given sensors current state changes (i.e. new Reading's state differs from Sensor's current state), you change the current state and add a row to the SensorStateLog showing the change in state. (Optionally, you could update the "prior" entry for that sensor with a "state ended" timestamp, but that's fussy code to write.)

CurrentSensorState in the Sensor table is denormalized data, but if properly maintained (and if you have millions of rows) it will make querying current state vastly more efficient and so may be worth the effort.

The obvious downside of all this is that Alerts are no longer an entity, and they become that much harder to track and identify. If these must be readily and immediately identifiable and usable, your third scheme won't do what you need it to do.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文