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

Posted

技术标签:

【中文标题】如何在mysql中存储类型可以是数字、日期或字符串的数据【英文标题】:How to store a data whose type can be numeric, date or string in mysql 【发布时间】:2016-06-19 01:47:54 【问题描述】:

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

一个数值。例如“CPU USAGE” = 55。意思是 55% 的 CPU 在 采用)。 触发了某些事件。例如“备份完成”。 状态:例如SQL Server 处于脱机状态。

我们想要存储这些观察结果(这些观察结果是事先不知道的,并且会在不重新编译的情况下动态添加到系统中)。

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

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

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

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

【问题讨论】:

“这是一个好的选择吗?” - 这取决于几个因素... 列总是具有相同的类型,不管它们存储了什么。无论如何,价值观本身应该“可能”不被反对。要确定特定设计是否合适,首先需要了解如何使用/查询数据。此外,如果需要进行复合测量,这样的设计就会失败。但是这些值本身是否与域模型相关? 耸耸肩 EAV 架构会导致很多麻烦。我添加了一个标签;关注它以获得更多建议。 【参考方案1】:

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

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

在数据库设计的所有阶段,首要考虑因素是数据完整性。 “答案可能并不总是正确的,但我们可以很快为您提供”不是任何商店都应该努力实现的目标。一旦数据完整性被锁定,如果性能成为问题,它可以得到解决。不要牺牲数据完整性,尤其是在解决可能不存在的问题时。

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

这似乎符合标准的“类型/子类型”模式,其中“观察”条目是类型,每种类型或类型的观察都是子类型,并建议某种形式的类型指示符字段,例如:

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

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

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

让我们检查一个可能存在于 Measurements 表中的示例元组:

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

为了使此元组存在于此表中,必须首先在 Observations 表中存在一个 ID 值为 1001 且观察类型值为“M”的匹配条目。 ID 值为 1001 的其他条目不能存在于 Observations 表或 Measurements 表中,并且根本不能存在于任何其他“种类”表(事件、状态)中。这适用于所有种类的表。

我会进一步建议为每种观察创建一个视图,该视图将提供每种与主观察表的连接:

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

任何仅用于测量的代码都只需要访问此视图而不是基础表。使用视图在应用程序代码和原始数据之间创建一堵抽象墙,极大地增强了数据库的可维护性。

现在创建另一种观察,例如“错误”,涉及到 ObservationKinds 表的简单插入语句:

F   Fault        A fault or error has been detected.

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

【讨论】:

亲爱的汤姆,非常感谢您的出色回答。有一件事我还不明白。为什么你使用 Kind 字段和 PK 的组合。我知道你在解释“为什么”,但我似乎无法理解。非常感谢 我仍在查看您的答案(并将其放入测试 mysql 数据库中)。我对为什么在两个表中重复 ObservationType 感到困惑? ID作为外键使用不一样吗? 很高兴您能够自己找到答案。对于其他可能想知道的人,将 ID 和 Kind 字段与唯一约束相结合意味着我们可以在 FK 中引用它们。 FK 在每个子类型表中定义。 Observations 表中的 Kind 可以是任何定义的 Kind 值,但只能是 Measurements 表中的“measurement”值、Events 表中的“event”值等等。这使得不可能定义一个Measurement Observation,但不小心将该ID输入到Events表(或除Measurements之外的任何其他表)中。 CHECK 被 MySQL 忽略。 ENUM 是一个合理的替代品。 是的,我对 MySQL 有所了解,但我一直忘记,因为我实际上并没有使用那个 dbms。这部分必须由触发器强制执行——或者切换到更好的 dbms。 ENUM 可以在类型表上工作,但子类型表仍然需要检查约束。实际上,类型表的更好选择是将字段设置为 FK 到列出所有类型的查找表。这将更易于维护——例如,在添加新类型时。【参考方案2】:

只需将其创建为 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

【讨论】:

【参考方案3】:

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

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

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

【讨论】:

以上是关于如何在mysql中存储类型可以是数字、日期或字符串的数据的主要内容,如果未能解决你的问题,请参考以下文章

mysql怎么把时间格式转化为数字

MySQL 数据类型

mysql 常见数据类型

MySQL 数据库字段类型使用说明

MySql中字符串转成日期

Mysql中的数据类型