我们正在开发一个监控系统。在我们的系统中,值由不同服务器上运行的代理报告。报告的观察结果可以是如下值:
我们想要存储这些观察结果(事先不知道,并且会在不重新编译的情况下动态添加到系统中)。
我们正在考虑在观察表中添加不同的列,如下所示:
IntMeasure -> INTEGER
FloatMeasure -> FLOAT
Status -> varchar(255)
因此,如果我们要存储的值是一个数字,我们可以根据类型使用IntMeasure或FloatMeasure。如果值是状态,我们可以存储状态文字字符串(如果我们决定添加状态(id,name)表,则存储状态ID)。
我们认为可以设计更正确但由于连接和动态表名取决于类型,可能会变慢和变暗?如果我们无法在查询中预先指定表,那么联接将如何工作?
答案 0 :(得分:6)
我还没有做过正式的研究,但根据我自己的经验,我猜测超过80%的数据库设计缺陷是通过将性能设计作为最重要(如果不仅仅是)考虑而产生的。
如果好的设计需要多个表,请创建多个表。不要自动假设连接是要避免的。它们很少是性能问题的真正原因。
首要考虑因素,首先是数据库设计的所有阶段,都是数据完整性。 “答案可能并不总是正确的,但我们可以很快得到它”,这不是任何商店应该努力的目标。一旦数据完整性被锁定,如果性能成为问题,就可以解决它。不要牺牲数据完整性,特别是要解决可能不存在的问题。
考虑到这一点,看看你需要什么。您有需要存储的观察结果。这些观察结果可以在属性的数量和类型上有所不同,可以是衡量标准的价值,事件的通知和状态的变化等,以及未来观察的可能性。
这似乎符合标准的“类型/子类型”模式,“观察”条目是类型,每种类型或种类的观察都是子类型,并建议某种形式的类型指示符字段,例如: / p>
create table Observations(
...,
ObservationKind char( 1 ) check( ObservationKind in( 'M', 'E', 'S' )),
...
);
但是在检查约束中对这样的列表进行硬编码具有非常低的可维护性级别。它成为模式的一部分,只能使用DDL语句进行更改。不是你的DBA会期待的东西。
在他们自己的查找表中有各种观察结果:
ID Name Meaning
== =========== =======
M Measurement The value of some system metric (CPU_Usage).
E Event An event has been detected.
S Status A change in a status has been detected.
(char字段也可以是int或smallint。我在这里使用char来说明。)
然后用PK和所有观察共有的属性填写Observations表。
create table Observations(
ID int identity primary key,
ObservationKind char( 1 ) not null,
DateEntered date not null,
...,
constraint FK_ObservationKind foreign key( ObservationKind )
references ObservationKinds( ID ),
constraint UQ_ObservationIDKind( ID, ObservationKind )
);
在Kind字段和PK的组合上创建一个唯一的索引可能看起来很奇怪,它本身就是唯一的,但请耐心等待。
现在每种类型或子类型都有自己的表格。请注意,每种观察都会得到一个表,而不是数据类型。
create table Measurements(
ID int not null,
ObservationKind char( 1 ) check( ObservationKind = 'M' ),
Name varchar( 32 ) not null, -- Such as "CPU Usage"
Value double not null, -- such as 55.00
..., -- other attributes of Measurement observations
constraint PK_Measurements primary key( ID, ObservationKind ),
constraint FK_Measurements_Observations foreign key( ID, ObservationKind )
references Observations( ID, ObservationKind )
);
对于其他类型的观察,前两个字段将是相同的,除了检查约束将强制值为适当的类型。其他字段的数量,名称和数据类型可能不同。
让我们检查一下测量表中可能存在的示例元组:
ID ObservationKind Name Value ...
==== =============== ========= =====
1001 M CPU Usage 55.0 ...
为了使此元组存在于此表中,匹配条目必须首先存在于Observations表中,ID值为1001,观察类型值为“M”。在Observations表或Measurements表中不能存在ID值为1001的其他条目,并且在任何其他“种类”表(事件,状态)中根本不存在。对于所有类型的表,它的工作方式相同。
我还建议为每种观察创建一个视图,它将提供各种类型的连接与主要观察表:
create view MeasurementObservations as
select ...
from Observations o
join Measurements m
on m.ID = o.ID;
任何仅适用于测量的代码都只需要点击此视图而不是基础表。使用视图在应用程序代码和原始数据之间创建抽象墙大大增强了数据库的可维护性。
现在创建另一种观察,例如“Error”,涉及到ObservationKinds表的简单Insert语句:
F Fault A fault or error has been detected.
当然,您需要为这些错误观察创建一个新表和视图,但这样做对现有表,视图或应用程序代码没有影响(当然,除了编写新代码以使用新观察)。
答案 1 :(得分:0)
只需将其创建为VARCHAR
即可这将允许您存储所需的任何数据。根据字段中的数字(例如
)进行查询要困难得多Select * from table where MyVARCHARField > 50 //get CPU > 50
但是,如果您认为要这样做,那么您需要每个项目的字段或广义表格,例如
Create Table
Description : Varchar
ValueType : Varchar //Can be String, Float, Int
ValueString: Varchar
ValueFloat: Float
ValueInt : Int
然后,当您填写数据时,您可以将您的值放在正确的字段中并选择如下。
Select Description ,ValueInt from table where Description like '%cpu%' and ValueInt > 50
答案 2 :(得分:0)
我有一个用于类似问题的两列。第一列用于数据类型,第二列包含数据作为Varchar。
第一列有代码(例如1 =整数,2 =字符串,3 =日期等),可以将它们组合起来比较值。 (例如,找到type = 1的最大整数)
我没有加入,但我认为你可以使用这种方法。如果明天会引入更多数据类型,它也会对你有帮助。