保持此架构清晰的最佳方法是什么?

时间:2011-03-30 15:18:44

标签: sql-server database polymorphic-associations

目前我正在开展一个RFID项目,每个标签都附在一个物体上。一个物体可以是一个人,一台电脑,一支铅笔,一个盒子或者老板心中的任何东西。 当然,每个对象都有不同的属性。

所以我正在尝试使用表格标签,我可以在其中保存系统中每个标签的注册表(注册标签)。另外一个表格,我可以将标签与对象相关联并描述其他一些属性,这就是我们所做的。 (没有真正的架构只是简化版本)

enter image description here

突然间,我意识到这个架构在几个表中可能有相同的标签。 例如,标签123可以同时在C和B中。这是不可能的,因为每个标签只能附加到一个对象上。

简单来说,我希望每个标记在数据库中不会出现多次。

我目前的做法 enter image description here

我真正想要的是什么 enter image description here

更新 是的,TagID由最终用户选择。此外,TagID由标签阅读器给出,TagID是128位数字。

新更新: 到目前为止的对象是:

- Medicament(TagID,comercial_name,generic_name,amount,...)

- 机器(TagID,名称,描述,型号,制造商......)

- 患者(TagID,firstName,lastName,birthday,...)

所有属性(列或其他任何名称)都非常不同。

更新后更新

我正在研究一个带有医院RFID标签的系统。每个RFID标签都附着在一个物体上,以便观察它们,不幸的是每个物体都有很多不同的属性。

对象可以是人,机器或药物,也可以是具有其他属性的新对象。

所以,我只想要一个灵活的切割器架构。这允许我引入新对象的类型,并让我轻松地向一个对象添加新属性。请记住,这个系统可能非常大。

示例:

Tag(TagID)
Medicine(generic_name, comercial_name, expiration_date, dose, price, laboratory, ...)
Machine(model, name, description, price, buy_date, ...)
Patient(PatientID, first_name, last_name, birthday, ...)

我们必须只为一个对象关联一个标记。

注意:我真的不会说(或者也写):P抱歉。这里不是母语。

7 个答案:

答案 0 :(得分:7)

您可以使用关系约束来强制执行这些规则。检查使用持久列来强制执行约束标记:{铅笔或计算机} 。此模型为您提供了极大的灵活性,可以对每个子表(人物,机器,铅笔等)进行建模,同时防止标记之间发生任何冲突。同样好,我们不必通过检查约束来使用触发器或udfs来强制执行关系。该关系内置于模型中。

diagram

create table dbo.TagType (TagTypeID int primary key, TagTypeName varchar(10));
insert into dbo.TagType
    values(1, 'Computer'), (2, 'Pencil');

create table dbo.Tag
(   TagId       int primary key, 
    TagTypeId   int references TagType(TagTypeId), 
    TagName     varchar(10),
    TagDate     datetime,
    constraint UX_Tag unique (TagId, TagTypeId)
)
go
create table dbo.Computer 
(   TagId       int primary key, 
    TagTypeID   as 1 persisted,
    CPUType     varchar(25),
    CPUSpeed    varchar(25), 
    foreign key (TagId, TagTypeID) references Tag(TagId, TagTypeID)
)
go
create table dbo.Pencil 
(   TagId       int primary key, 
    TagTypeId   as 2 persisted,
    isSharp     bit,
    Color       varchar(25),
    foreign key (TagId, TagTypeID) references Tag(TagId, TagTypeId)
)
go



-----------------------------------------------------------
-- create a new tag of type Pencil:
-----------------------------------------------------------
insert into dbo.Tag(TagId, TagTypeId, TagName, TagDate)
    values(1, 2, 'Tag1', getdate());

insert into dbo.Pencil(TagId, isSharp, Color)
    values(1, 1, 'Yellow');

-----------------------------------------------------------
-- try to make it a Computer too (fails FK)
-----------------------------------------------------------
insert into dbo.Computer(TagId, CPUType, CPUSpeed)
    values(1, 'Intel', '2.66ghz')

答案 1 :(得分:3)

使用TagID的PK身份插入标记表。 这将确保每个TagID只出现一次......

然后在Tag Table中有一个TagType列,它可以是自由格式(TableName),也可以更好,但有一个带有条目A,B,C的TagType表,然后在Tag中指向TagType的FK。

我会将Tag属性移动到表A,B,C中以最小化Tag中的额外数据或在Tag和A,B和C之间有一系列连接表

编辑: 假设在创建对象时创建了TagID,这将正常工作(首先插入Tag以获取TagID并使用IDENTITY_INSERT捕获它) 这假设用户无法编辑TagID本身。

如果用户可以选择TagID,那么仍然使用TagID和TagID,但是有另一个名为DisplayID的字段,用户可以在其中键入数字。只需在Tag.DisplayID ....上加上一个独特的约束。

编辑: 你需要什么属性,它们可以为空?如果它们对于A,B和C是不同的那么将它们放在A,B和C中会更清楚,特别是如果A和B可能有一些而不是C ......

答案 2 :(得分:3)

与Raz谈谈清理他正在做的事情。他想要的是一种灵活的方式来存储与标签相关的属性。标签可以是多种类型的对象之一,每个对象都有一个特定的属性列表。他还希望能够添加对象/属性而无需更改架构。这是我提出的模型:

Model

答案 3 :(得分:2)

如果每个标签只能在a,b或c中只有一次,我只需将a,b和c组合成一个表。如果你举一个你想要收集的内容的例子,那么更容易让你更好地了解如何构建你的模式。

对我来说,从我所读到的,听起来你有一个标签列表和一个对象列表,你需要为一个对象分配一个标签。如果是这种情况,我会有一个标签表,对象表和一个ObjectTag表。在对象选项卡表中,您将拥有标记表的外键和对象表的外键。然后你在标记外键上创建一个唯一索引,现在你已经强制要求只使用一次标记。

答案 4 :(得分:2)

我会用你的原始结构解决这个问题。与解析复杂数据结构相比,关系数据库在聚合/组合原子数据方面要好得多。

  • 将每个“可标记”对象类型的设计保留在自己的表中。数据类型,检查约束,默认值等仍然可以通过这种方式轻松实现。另外,继续将每个对象表中的FK定义为Tags表。

  • 我假设你已经有了这个,但如果你在每个对象表(A,B,C等)的TagId列上放置一个唯一约束,那么你可以保证其中的唯一性该对象类型。

  • 如果实现为单独的表,则没有内置的SQL Server约束来保证所有对象类型之间的唯一性。因此,您必须进行自己的验证。对象表上的INSTEAD OF触发器可以干净利落地执行此操作。

首先,创建一个视图以访问所有对象表中的TagId列表。

CREATE VIEW TagsInUse AS
    SELECT A.TagId FROM A
    UNION
    SELECT B.TagId FROM B
    UNION
    SELECT C.TagId FROM C
;

然后,为每个对象表定义一个INSTEAD OF触发器来测试你的TagId。

CREATE TRIGGER dbo.T_IO_Insert_TableA ON dbo.A
    INSTEAD OF INSERT
    AS
    IF EXISTS (SELECT 0 FROM dbo.TagsInUse WHERE TagId = inserted.TagId)
    BEGIN;
        --The tag(s) is/are already in use. Create the necessary notification(s).
        RAISERROR ('You attempted to re-use a TagId. This is not allowed.');
        ROLLBACK
    END;
    ELSE
    BEGIN;
        --The tag(s) is/are available, so proceed with the INSERT.
        INSERT INTO dbo.A (TagId, Attribute1, Attribute2, Attribute3)
            SELECT  i.TagId, i.Attribute1, i.Attribute2, i.Attribute3
            FROM    inserted AS i
        ;
    END;
GO

请记住,出于维护和性能原因,您也可以(并且可能应该)将此IF EXISTS测试封装在T-SQL函数中。

您可以编写补充存储过程,以查找与TagId相关联的对象类型。

赞成

  • 您仍在利用SQL Server的数据完整性功能,这些功能都非常快速且可自我记录。不要低估数据类型的有用性。

  • 视图是域的封装,必须是唯一的,而不需要组合底层的属性集。现在,您不必编写任何杂乱的代码来破译对象的类型。您可以根据哪个表包含匹配的标记来确定。

  • 您的选择仍然开放......

因为您没有将所有内容存储在EAV友好的nvarchar(300)列中,所以您可以调整数据类型,以获取对每个属性最有意义的内容。

如果遇到任何性能问题,可以索引视图。

如果您需要平衡事情并帮助并行磁盘I / O,您(或您的DBA)可以将对象表移动到不同磁盘上的不同文件组。可以将其视为水平分区的一种形式。例如,如果您将药物容器的RFID标签应用于患者的8倍,则可以将药物表放在不同的磁盘上,而无需创建单片表所需的分区功能(一个表格)适用于所有类型)。

如果您需要最终对表进行垂直分区(将数据存档到只读分区),则可以更轻松地为每种对象类型创建分区功能。这在业务规则执行时非常有用

最重要的是 ,根据对象类型实施不同的业务规则更多更简单。你不必实现任何讨厌的条件逻辑,如“IF type ='needle'THEN ... ELSE IF type ='patient'那么......如果......”。如果需要应用不同的规则,则将它们应用于相关的对象表,而不必测试“类型”值。

缺点

  • 必须保持触发器。但是,无论如何都必须在您的应用程序中完成,因此您在数据库中执行相同的数据完整性检查。这意味着您将不会有额外的网络开销,这将适用于使用此数据库的任何应用程序。

答案 5 :(得分:1)

您所描述的是经典的“每类型表”ORM映射。实体框架内置了对此的支持,您应该深入研究。

否则,我认为大多数数据库都没有简单的完整性约束,这些约束是通过多个表的主键强制执行的。

但是,有没有理由不能只使用一个标签表来保存所有字段?使用类型字段来保存对象的类型。 NULL所有不相关的字段 - 这样它们不消耗磁盘空间。你最终会得到更少的表(只有一个),你可以将它作为一个单一的连贯对象来维护;它还使您可以编写更少的SQL查询来处理可能跨越多个对象类型的标记。

将它实现为一个单独的表也可以节省磁盘空间,因为您可以实现继承层 - 例如,“patient”和“doctor”和“nurse”可以是三种不同的对象类型,每种类型都有相似的字段(例如firstname,lastname等)和一些独特的字段。现在你需要三个表,有重复的字段。

添加对象类型时也更简单。之前,您需要添加一个新表,并复制一些跨多个对象类型的SQL语句。现在,您只需要将新字段添加到同一个表中(可能重用一些)。您需要更改的SQL要少得多。

你不会使用单个表的唯一原因是当字段数量太大而无法放入SQL-Server页面(我相信是8K)时。然后SQL会抱怨并且不允许您添加任何其他字段。在这种情况下,解决方案是采用ORM工具(如实体框架),然后“重用”字段。例如,如果“Field1”仅由对象类型#1使用,则没有理由说明对象类型#3也不能使用它来存储某些内容。您只需要能够在程序中区分它。

答案 6 :(得分:0)

您可以使用Tags表,使其可以指向任何这些表,并且可以包含一个Type,告诉您它是哪个表

Tags
-
ID
Type (A,B, or C)
A (nullable)
B (nullable)
C (nullable)

A
-
ID
(other attributes)