Mac*_*y18 4 mysql database-design
我的tasks
数据库中有一个表,在感兴趣的业务领域中,任务可以有多种状态:“打开”、“开始”、“审查中”和“已完成”。
尽管在同一个表中有“打开”和“开始”,但使用标志标识,但出于某种原因,我很早就决定将“审查中”和“已完成” 任务放在他们自己的单独表中。虽然这似乎是一个好主意,可以轻松查询特定事物,但当我考虑为任务实现注释功能时,我意识到值会在三个表中发生变化。task_id
我做错了吗?
当前的考虑
我想到的一些解决方案是重做组织并将所有内容放在一张桌子上,使用标志来纯粹区分状态(这将导致大量的返工);创建某种UUID
可以跨表转移的东西 - 尽管我认为如果这样做可能会导致性能问题;最后,将IDs
跨表设置为不再自动递增,并简单地“继承”第一个task
表(自动递增)的原始值。
在这种情况下做了什么?我不是最精通数据库设计的人,而且我在进行过程中正在编造这一点。
任何帮助将不胜感激。
对@MDCCL 通过评论提出的进一步信息请求的回应
因此,状态(或状态)一个的任务,可以随着时间而改变,对不对?同一个具体的任务可以出现多少次相同的状态,比如“打开”?它会根据相关任务呈现的状态类型而变化吗?
是的,一个task.state理论上最多可以改变四次。我不会详细介绍逻辑,但最长的链将是“begun” ? “completed” ? “review” ? “completed”
. 每次,目前,它们都被放入另一张桌子。
例外的是,无论它是否“开始”,用户都可能会无限地放弃然后重新执行任务——至少在理论上是这样。这至少得益于“开始”状态是一个标志这一事实。
好的,但是某个任务将始终呈现一个特定的“当前”状态,对吗?例如,当
task
一行被插入时,它应该伴随一个特定的state
值,我假设它是“打开的”。稍后,此类任务的状态将演变为仅其他可能选项之一,即“开始”或“审核中”或“已完成”。
是的,完全正确。一个任务永远是在一个阶段,不能在多个。
一旦某个特定任务被视为“已完成”,它是否可以更改为另一种状态?
是的,如果达到某些参数,可以将其发送回“审查”。从“审查”,它可以回到“开始”或“完成”。
就我看到它,事实上,(一)具体的任务可呈现不同的国家在时间上造成采用不同的点(B)的创建是要包含一个表中的时间序列,因为我将在下面详细。
为了定义关系数据库的结构和约束,首先要准确地识别和制定相应的业务领域规则(即描绘概念模式),同时考虑到相关的实体类型、属性和相应的 互连。 . 这样,在所考虑的场景中特别重要的一些公式是:
因此,基于上述公式,我创建了 IDEF1X †图,如图 1所示:
如您所见,Task
并被State
描述为单独的实体类型,每个实体类型都有自己的一组属性(或属性)和自己的(直接)关联(或关系),它们通过相应的动词短语、行、基数和 FOREIGN 表示KEY (FK) 标记。
命名的实体类型StateSpecification
与之前提出的两个相关联,是我将在下面解释的解决方案的核心方面。
我根据上面的 IDEF1X 图,通过以下 DDL 结构塑造了一个说明性逻辑设计:
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also, you should make accurate tests to define the most
-- convenient physical implementation settings; e.g.,
-- a good indexing strategy based on query tendencies.
-- As one would expect, you are free to use your
-- preferred or required naming conventions.
CREATE TABLE Task (
TaskNumber INT NOT NULL,
Description CHAR(90) NOT NULL,
Etcetera CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Task_PK PRIMARY KEY (TaskNumber)
);
CREATE TABLE State (
StateCode CHAR(1) NOT NULL,
Name CHAR(30) NOT NULL,
Description CHAR(60) NOT NULL,
--
CONSTRAINT State_PK PRIMARY KEY (StateCode),
CONSTRAINT State_AK1 UNIQUE (Name), -- ALTERNATE KEY.
CONSTRAINT State_AK2 UNIQUE (Description) -- ALTERNATE KEY.
);
CREATE TABLE StateSpecification (
TaskNumber INT NOT NULL,
SpecifiedDateTime DATETIME NOT NULL,
StateCode CHAR(1) NOT NULL,
--
CONSTRAINT StateSpecification_PK PRIMARY KEY (TaskNumber, SpecifiedDateTime), -- Composite PRIMARY KEY.
CONSTRAINT StateSpecification_to_Task_FK FOREIGN KEY (TaskNumber)
REFERENCES Task (TaskNumber),
CONSTRAINT StateSpecification_to_State FOREIGN KEY (StateCode)
REFERENCES State (StateCode)
);
CREATE TABLE TaskComment (
TaskNumber INT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
Body TEXT NOT NULL,
--
CONSTRAINT TaskComment_PK PRIMARY KEY (TaskNumber, CreatedDateTime),
CONSTRAINT TaskComment_to_Task FOREIGN KEY (TaskNumber)
REFERENCES Task (TaskNumber)
);
--
--
Run Code Online (Sandbox Code Playgroud)
状态表
State
前面说明的表起到了查找的作用,它包含五行(根据您提供的五个州样本):
+-—————————-+-—————————-+-———————————————-+ | 状态代码| 姓名 | 描述 | +-—————————-+-—————————-+-———————————————-+ | 哦| 打开 | 表示…… | +-----------+-----------+-----------------+ | 乙 | 开始 | 表示…… | +-----------+-----------+-----------------+ | R | 在审查 | 表示…… | +-----------+-----------+-----------------+ | C | 已完成 | 表示…… | +-----------+-----------+-----------------+
请注意保留值的列上的 PRIMARY KEY (PK) 约束的定义,这些值同时是meaninfgul(关于最终用户和技术人员的解释)和小(就物理实现级别的字节而言),这使得它在例如数据检索方面既可读又快速。
如果有新的业务领域的国家出现,你当然可以插入相应的行插入到State
表。
状态规格表
现在,假设任务没有。1750以表中保存的(无序)行的形式呈现以下状态历史StateSpecification
:
+-———————————-+-———————————————————————————-+-————————— -+ | 任务编号| 指定日期时间 | 状态代码| +-———————————-+-———————————————————————————-+-————————— -+ | 1750 | 2016-12-01 16:58:12.000 | 哦| +------------+-------------------------+--------- -+ | 1750 | 2016-12-02 09:12:05.000 | 乙 | +------------+-------------------------+--------- -+ | 1750 | 2016-12-04 10:57:01.000 | R | +------------+-------------------------+--------- -+ | 1750 | 2016-12-07 07:33:08.000 | C | +------------+-------------------------+--------- -+ | 1750 | 2016-12-08 12:12:09.000 | R | +------------+-------------------------+--------- -+ | 1750 | 2016-12-08 19:46:01.000 | 乙 | +------------+-------------------------+--------- -+ | 1750 | 2016-12-09 06:24:07.000 | R | +------------+-------------------------+--------- -+ | 1750 | 2016-12-11 07:24:07.000 | C | +------------+-------------------------+--------- -+
如图所示,SpecifiedDateTime
列中的每个值表示给定任务(通过TaskNumber
)开始呈现特定状态(凭借StateCode
)的精确时间点。
看到该StateCode
列保留了具有非常明确意图的值,它有助于节省 JOIN 子句的使用。这是极为重要的明确,这并不能意味着连接是什么不利(他们,事实上,在任何关系型数据库基础和强有力的手段),但是,只要合适,降低了系统的宝贵资源的消耗是坚决实用.
评论表
Comment
如上所述,该表可以很容易地Task
通过 FK 约束定义与该表相关联,因为 a 的 PK 值Task
将保持不变,无论其状态是否经历零次、一次或多次修改。
我假设,在相关的业务上下文中,评论(a) 将始终取决于任务事件的存在,并且 (b) 将始终属于同一单个任务事件;因此,我Comment
使用复合 PK定义了该表,该组合 PK 包含 PK 列Task
以及创建DateTime
特定项的确切位置Comment
。除此之外,您可能想评估系统控制代理的列(例如,MySQL 中具有AUTO_INCREMENT属性的列)到此表的附件,它可能会或可能不会提供更好的执行性能(可能不会),所以进行一些彻底的测试会澄清情况(a非- 数据添加,就像包含系统控制代理的列的情况一样,应始终进行充分评估和证明)。
对于所讨论的场景,很可能需要一些重要的数据点,但它们应该作为派生(或计算)值获得。
例如,如果你需要得到CurrentState
的任务没有。1750,您可以使用下一个 SELECT 语句派生它:
SELECT T.TaskNumber,
SS.StateCode AS CurrentStateCode,
SS.SpecifiedDateTime
FROM Task T
JOIN StateSpecification SS
ON T.TaskNumber = SS.TaskNumber
WHERE T.TaskNumber = 1750 -- You can provide a parameter instead of a fixed value.
AND SS.SpecifiedDateTime = (
SELECT MAX(SpecifiedDateTime)
FROM StateSpecification InnerSS
WHERE T.TaskNumber = InnerSS.TaskNumber
);
Run Code Online (Sandbox Code Playgroud)
当您需要CurrentState
为所有任务获取 时,您可以声明如下语句:
SELECT T.TaskNumber,
SS.StateCode AS CurrentStateCode,
SS.SpecifiedDateTime
FROM Task T
JOIN StateSpecification SS
ON T.TaskNumber = SS.TaskNumber
WHERE SS.SpecifiedDateTime = (
SELECT MAX(SpecifiedDateTime)
FROM StateSpecification InnerSS
WHERE T.TaskNumber = InnerSS.TaskNumber
);
Run Code Online (Sandbox Code Playgroud)
如果您必须导出StartDate
与任务编号相对应EndDate
的所有和。1750,您可以使用:StateSpecifications
SELECT T.TaskNumber,
T.Description,
SS.StateCode,
SS.SpecifiedDateTime AS StartDateTime,
(
SELECT MIN(SpecifiedDateTime)
FROM StateSpecification InnerSS
WHERE T.TaskNumber = InnerSS.TaskNumber
AND InnerSS.SpecifiedDateTime > SS.SpecifiedDateTime
) AS EndDateTime
FROM Task T
JOIN StateSpecification SS
ON T.TaskNumber = SS.TaskNumber
WHERE T.TaskNumber = 1750 -- You can provide a parameter instead of a fixed value.
ORDER BY StartDateTime DESC;
Run Code Online (Sandbox Code Playgroud)
如果您必须获得StartDate
与EndDate
所有的StateSpecifications
,即整个时期中,他们目前的:
SELECT T.TaskNumber,
T.Description,
SS.StateCode,
SS.SpecifiedDateTime AS StartDateTime,
(
SELECT MIN(SpecifiedDateTime)
FROM StateSpecification InnerSS
WHERE T.TaskNumber = InnerSS.TaskNumber
AND InnerSS.SpecifiedDateTime > SS.SpecifiedDateTime
) AS EndDateTime
FROM Task T
JOIN StateSpecification SS
ON T.TaskNumber = SS.TaskNumber
ORDER BY StartDateTime DESC;
Run Code Online (Sandbox Code Playgroud)
查看定义
自然地,上面显示的一个或多个 SELECT 语句可以设置为 VIEW,这样您就可以直接从单个表中获得相关信息(派生的,是的,但它仍然是一个表) .
演示
我在此 SQL Fiddle(在 MySQL 5.6 上运行)和此 db<>fiddle(在 Microsoft SQL Server 2014 上运行,如果有人感兴趣的话)中创建了代码的现场演示。
我想我将不得不执行此修改,并认为系统将受益于
StateSpecification
表的附加功能。它表明良好的数据库设计可以满足我们的所有需求,而不会使事情变得混乱。
我完全同意你的第二个断言。设计得当(概念上、逻辑上和物理上)的数据库通常会成为软件开发项目中的关键元素。如果关系数据库准确地反映了感兴趣的业务上下文,那么它可以——简而言之——大大简化其维护并提供广泛的多功能性。这就是为什么它是仔细分析相关的结构非常有用的东西,观察他们来讲是什么,他们是在现实世界中,然后用精密在数据库中执行代表他们。
数据本身就是一种非常有价值的组织资产,因此,它应该像这样进行管理。实现上述目标的可靠方式是采用有可靠理论支持的技术手段,在数据管理领域,没有什么比关系模型更可靠的了。这与EF Codd 博士1981 年的图灵奖演讲一致,题为Relational Database: A Practical Foundation for Productivity。
另外,为了澄清一下
StateSpecification
,必须在每次状态更改时手动更新该表,对吗?
基本上是的,StateSpecification
每次任务呈现状态更改时都必须更新表格。此过程应通过引入新StateSpecification
行的 INSERT 操作来执行,该行应包含指向Task
应用行的 FK 值。
它是适当时候的应力,当Task
行被插入,它必须呈现StateSpecification
指示所述状态视为“打开”(通过“O”值的力保留在StateCode
柱),因此你应该利用良好定义的ACID TRANSACTIONS以便将Task
行及其StateSpecification
对应项视为一个单元。以这种方式,两个各自的 INSERT 操作作为一个整体可以成功也可以失败,从而使所涉及的数据与识别的业务规则保持一致。
你可能会找到帮助
因为它包括一个具有相似特征的案例。
尾注
† 信息建模集成定义( IDEF1X ) 是一种非常值得推荐的数据建模技术,美国国家标准与技术研究院(NIST)于 1993 年 12 月将其确立为标准。它牢固地基于 (a) 关系模型的创始人,即 EF Codd 博士撰写的早期理论工作;关于 (b)实体关系视图,由PP Chen 博士开发;以及 (c) 逻辑数据库设计技术,由 Robert G. Brown 创建。
归档时间: |
|
查看次数: |
1112 次 |
最近记录: |