如何在mysql中存储类型可以是数字、日期或字符串的数据

How to store a data whose type can be numeric, date or string in mysql

我们正在开发一个监控系统。在我们的系统中,值由不同服务器上的代理 运行 报告。报告的观察值可以是这样的值:

我们想要存储这些观察结果(这些观察结果是事先不知道的,将动态添加到系统中而无需重新编译)。

我们正在考虑向观察 table 添加不同的列,如下所示:

IntMeasure -> INTEGER
FloatMeasure -> FLOAT
Status -> varchar(255)

因此,如果我们希望存储的值是一个数字,我们可以根据类型使用 IntMeasure 或 FloatMeasure。如果该值是状态,我们可以存储状态文字字符串(或者如果我们决定添加 Statuses(id, name) table,则为状态 id)。

我们认为有可能有一个更正确的设计,但可能会由于连接和动态 table 名称而变得缓慢和黑暗,具体取决于类型?如果我们不能在查询中提前指定 table,连接将如何工作?

我没有做过正式的研究,但根据我自己的经验,我猜测超过 80% 的数据库设计缺陷是在设计时将性能作为最重要(如果不是唯一)的考虑因素而产生的。

如果好的设计需要多个 table,请创建多个 table。不要自动假设连接是应该避免的。它们很少是性能问题的真正原因。

在数据库设计的所有阶段,首要考虑的是数据完整性。 "The answer may not always be correct, but we can get it to you very quickly" 不是任何商店都应该努力实现的目标。一旦数据完整性被锁定,如果性能成为问题,它就可以得到解决。不要牺牲数据完整性,尤其是解决可能不存在的问题。

考虑到这一点,看看你需要什么。您有需要存储的观察结果。这些观察在属性的数量和类型上可能会有所不同,并且可以是诸如测量值、事件通知和状态变化等之类的东西,并且有可能添加未来的观察。

这似乎符合标准 "type/subtype" 模式,"Observation" 条目是类型,每种类型或观察类型是子类型,并建议某种形式的类型指示符字段如:

create table Observations(
   ...,
   ObservationKind  char( 1 ) check( ObservationKind in( 'M', 'E', 'S' )),
   ...
);

但是在检查约束中对这样的列表进行硬编码具有非常低的可维护性级别。它成为模式的一部分,只能使用 DDL 语句进行更改。不是您的 DBA 会期待的东西。

所以在自己的查找中有各种观察table:

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 和所有观察结果共有的属性填写观察结果 table。

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 的组合上创建唯一索引可能看起来很奇怪,它本身是唯一的,但请耐心等待。

现在每个种类或子类型都有自己的 table。请注意,每种观察都会得到 table,而不是数据类型。

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 )
);

前两个字段对于其他类型的观察是相同的,除了检查约束将值强制为适当的类型。其他字段的数量、名称和数据类型可能不同。

让我们检查一个可能存在于测量中的示例元组 table:

ID    ObservationKind  Name       Value  ...
====  ===============  =========  =====
1001  M                CPU Usage  55.0   ...

为了使此元组存在于此 table 中,匹配条目必须首先存在于观察 table 中,ID 值为 1001,观察类型值为 'M'. ID 值为 1001 的其他条目不能存在于观察 table 或测量 table 中,并且根本不能存在于任何其他 "kind" table 中(事件、状态)。这对所有类型 tables.

的工作方式相同

我进一步建议为每种观察创建一个视图,这将提供每种类型与主要观察的连接 table:

create view MeasurementObservations as
    select ...
    from   Observations o
    join   Measurements m
        on m.ID = o.ID;

任何仅适用于测量的代码都需要只点击此视图而不是底层 tables。使用视图在应用程序代码和原始数据之间创建抽象墙大大增强了数据库的可维护性。

现在创建另一种观察,例如 "Error",涉及到 ObservationKinds 的简单 Insert 语句 table:

F   Fault        A fault or error has been detected.

当然,您需要为这些错误观察创建一个新的table和视图,但是这样做不会对现有的table、视图或应用程序代码产生影响(除了,当然,编写新代码以处理新观察结果)。

只需将其创建为 VARCHAR

这将允许您在其中存储您需要的任何数据。根据

等字段中的数字进行查询要困难得多
Select * from table where MyVARCHARField > 50 //get CPU > 50

但是,如果您认为自己想要这样做,那么您要么需要每个项目一个字段,要么需要一个通用的 table,例如

Create Table

Description : Varchar
ValueType : Varchar //Can be String, Float, Int
ValueString: Varchar
ValueFloat: Float
ValueInt : Int

然后当您填写数据时,您可以将您的值放在正确的字段中,然后 select 像这样。

Select Description ,ValueInt from table where Description like '%cpu%' and ValueInt > 50

我曾用过两列来解决类似的问题。第一列用于数据类型,第二个值包含作为 Varchar 的数据。

第一列有代码(例如 1= 整数,2 = 字符串,3 = 日期等等),可以组合这些代码来比较值。 (例如,找到 type=1 的最大整数)

我没有加入,但我认为你可以使用这种方法。如果明天引入更多的数据类型,它也会对你有所帮助。