Mar*_*ark 6 sql database database-design relational-database
我最近问了几个关于数据库设计的问题,可能太多了;-)但是我相信我正在慢慢地用我的设计解决问题的核心并且慢慢地将它煮沸了.我仍在努力解决关于"警报"如何存储在数据库中的几个决定.
在该系统中,警报是必须被确认,采取行动等的实体.
最初我将读数与这样的警报相关联(非常简化): -
[Location]
LocationId
[Sensor]
SensorId
LocationId
UpperLimitValue
LowerLimitValue
[SensorReading]
SensorReadingId
Value
Status
Timestamp
[SensorAlert]
SensorAlertId
[SensorAlertReading]
SensorAlertId
SensorReadingId
Run Code Online (Sandbox Code Playgroud)
最后一个表是将读数与警报相关联,因为它是指示传感器处于警报状态的读数.
这种设计的问题在于它允许来自许多传感器的读数与单个警报相关联 - 而每个警报仅针对单个传感器,并且应该仅具有与其相关联的传感器的读数(我应该担心数据库允许这虽然?).
我想简化一些事情,为什么还要烦扰SensorAlertReading表呢?相反,我可以这样做:
[Location]
LocationId
[Sensor]
SensorId
LocationId
[SensorReading]
SensorReadingId
SensorId
Value
Status
Timestamp
[SensorAlert]
SensorAlertId
SensorId
Timestamp
[SensorAlertEnd]
SensorAlertId
Timestamp
Run Code Online (Sandbox Code Playgroud)
基本上我现在没有将读数与警报相关联 - 相反,我只知道特定传感器的开始和结束时间之间的警报是活动的,如果我想查找该警报的读数,我可以做.
显然缺点是我不再有任何约束阻止我删除警报期间发生的读数,但我不确定约束是否必要.
现在从外面看作为开发人员/ DBA,这会让你想生病还是看起来合情合理?
有没有其他方法可以让我失踪?
谢谢.
编辑: 这是另一个想法 - 它以不同的方式工作.它存储每个传感器状态变化,从表格中的正常变为警报,然后读数简单地与特定状态相关联.这似乎解决了所有问题 - 你怎么想?(我唯一不确定的是调用表"SensorState",我不禁认为有一个更好的名字(也许是SensorReadingGroup?): -
[Location]
LocationId
[Sensor]
SensorId
LocationId
[SensorState]
SensorStateId
SensorId
Timestamp
Status
IsInAlert
[SensorReading]
SensorReadingId
SensorStateId
Value
Timestamp
Run Code Online (Sandbox Code Playgroud)
必须有一个优雅的解决方案!
数据模型
我认为您的数据模型应如下所示:?传感器数据模型?.(第2页与您的其他问题有关的历史).
不熟悉关系建模标准的读者可以找到?IDEF1X表示法?有用.
业务(评注中制定的规则)
我确实找到了一些现在已经过时的早期业务规则,所以我删除了它们
这些可以在关系中"读取"(与数据模型相邻).业务规则和所有隐含的参照和数据完整性可以在任何ISO SQL数据库中实现,因此由RULES,CHECK约束条件保证.这是IDEF1X的演示,用于开发Relational键和实体和关系.请注意,动词短语不仅仅是蓬勃发展.
除了三个参考表之外,唯一的静态识别实体是Location,NetworkSlave和User.传感器是系统的核心,所以我给它自己的标题.
地点
Location包含一对多SensorsLocation可能有一个记录器NetworkSlave
用户
User可以保持零一对多Locations User可以保持零一对多Sensors User可以保持零一对多NetworkSlaves User可以执行零到多Downloads User可以做零到多Acknowledgements,一个在一个Alert User可以采用零到多Actions,每一个ActionType 传感器
A SensorType安装为零到多Sensors
A Logger(房屋和)收集Readings一个LoggerSensor
阿Sensor是任一个NetworkSensor 或一个LoggerSensor
NetworkSensor记录
. ReadingsNetworkSlaveLogger是周期性的Downloaded一对多次
LoggerSensor记录
. ReadingsLoggerReading可以被视为Alert一个AlertType
AlertType情况可能发生在零到很多ReadingsAlert可以是一个Acknowledgement由一个用户.Acknowledgement,一个Action,一个ActionType,一个可以关闭User
ActionType可以采取零一对多Actions回应评论
Id在移动的所有内容上粘贴列会干扰标识符的确定,标识符是为数据库提供关系"权力"的自然关系键.它们是代理键,这意味着额外的键和索引,它阻碍了关系的力量; 这会产生比其他必要更多的连接.因此,只有当Relational键变得太麻烦而无法迁移到子表(并接受强加的额外连接)时,我才使用它们.
可空键是Unnormalised数据库的典型症状.数据库中的空白对性能来说是个坏消息; 但FK中的Nulls意味着每个表都做了太多的事情,含义太多,结果是代码非常糟糕.适合喜欢"重构"数据库的人; 对于Relational数据库完全没有必要.
已解决:Alert可能是Acknowledged; 一种Acknowledgement可能Actioned.
该行上方的列是主键(请参阅符号文档). SensorNo是一个序列号LocationId; 参考业务规则,外面没有意义Location; 两列一起形成PK.当您准备好插入传感器时(在检查了尝试是否有效之后等),它的推导如下.这不包括LoggerSensors,它们为零:
INSERT Sensor VALUES (
@LocationId,
SensorNo = ( SELECT ISNULL(MAX(SensorNo), 0) + 1
FROM Sensor
WHERE LocationId = @LocationId
)
@SensorCode
)对于准确性或改进的意思,我已经改变了NetworkSlave monitors NetworkSensor到NetworkSlave collects Readings from NetworkSensor.
检查约束.该NetworkSensor和LoggerSensor是独家亚型Sensor,它们的完整性可以通过CHECK约束来设定.Alerts, Acknowledgements并且Actions不是子类型,但它们的完整性是通过相同的方法设置的,因此我将它们列在一起.
数据模型中的每个关系都作为FOREIGN KEY(child_FK_columns)中的子(或子类型)中的CONSTRAINT实现REFERENCES Parent(PK_columns)
需要鉴别器来识别a Sensor是哪个子类型.这是SensorNo = 0为了LoggerSensors; 并且非零NetworkSensors.
NetworkSensors和LoggerSensors受限于NetworkSlave和Logger; 以及传感器.NetworkSensor,包括CHECK约束以确保SensorNo非零在LoggerSensor,包含CHECK约束以确保SensorNo为零
存在Acknowledgements并Actions受到所识别的FK约束的约束(一个Acknowledgement不能存在而不存在Alert;一个Action不能存在而不存在Acknowledgement).相反,Alert没有Acknowledgement处于未被承认的状态; a Alert,Acknowledgement但是没有Action处于公认但未采取行动的状态..
警报.这种(实时监控和警报)应用程序设计中的概念是许多小程序,独立运行; 所有使用数据库作为事实的单一版本.有些程序插入rows(Readings, Alerts); 其他程序轮询数据库是否存在此类行(并发送SMS消息等;或者手持单元仅接收与该单元相关的警报).从这个意义上说,db可以被描述为一个消息框(一个程序放入行,另一个程序读取和操作).
假设是,Readings由于Sensors被记录为"实时" NetworkSlave,并且每分钟左右Readings都插入一组新的.后台进程定期执行(每分钟或其他),这是主要的"监视器"程序,它在循环中将有许多功能.一个这样的函数将是监视Readings和产生Alerts自上一次迭代(程序循环)以来发生的.
以下代码段将在循环内执行,每个AlertType一个.这是一个经典的投影:Run Code Online (Sandbox Code Playgroud)
So an
-- 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.UpperLimitAlert绝对是一个事实,它在数据库中作为一行存在.随后,可能是Acknowledged由一个User(另一行/事实),并且Actioned具有ActionType通过User.
其他的(通过投影法创作),即.一般和不变的情况,我只会指代Alert一行Alert; 创建后的静态对象.
关注重新变化Users.这已经得到了解决,如下所述.在我的(昨日修订)答案的顶部,我声明主要的识别元素是静态的.我重新排列了业务规则以提高清晰度.
由于你提到的原因,User.Name它不是一个好的PK User,虽然它仍然是备用键(唯一)和用于人类交互的键.
User.Name不能重复,不能多于一个Fred; 有可能的FirstName-LastName; 二Fred Bloggs,但不是User.Name.我们的第二个Fred需要选择另一个User.Name.请注意已识别的指数.
UserId是永久记录,它已经是PK了.永远不要删除User,它具有历史意义.事实上,FK约束将阻止你(永远不要在真正的数据库中使用CASCADE,这是纯粹的精神错乱).不需要代码或触发器等.
或者(删除Users谁从来没有任何东西,因此释放User.Name使用)允许,因为没有侵犯FK(即只要删除UserId是不是引用的Download, Acknowledgement, Action).
要确保只有Users当前执行的人Actions,IsObsolete在User(DM Updated)中添加一个布尔值,并在询问任何函数(报告除外)时检查该列您可以实现UserCurrent仅返回那些的View Users.
这同样适用于Location和NetworkSlave.如果您需要区分当前与历史,请告诉我,我也会添加IsObsolete它们.
我不知道:您可以定期清除古代历史数据库,删除(例如)超过10年的行.这必须首先从底部(表格)完成,处理关系.
随意问的问题.
请注意,IDEF1表示法文档已展开.
| 归档时间: |
|
| 查看次数: |
3070 次 |
| 最近记录: |