假设您有一个像INVENTORY_ITEM这样的关系数据库表。它是通用的,因为库存中的任何东西都需要记录。现在我们可以说有大量不同类型的库存,每种不同的类型可能都有他们想要跟踪的唯一字段(例如,叉子可能跟踪尖齿的数量,但冰箱不会用于该字段)。这些字段必须是每个类别类型的用户可定义的。
有很多方法可以解决这个问题:
这是最好的做法吗?在我看来,选项4是干净的,但不允许您轻松搜索元数据。我之前使用过3的变体,但只在一个行数非常少的表上,所以性能不是问题。在我看来,2总是一个好主意,但它不适合自动生成的实体框架,所以你必须从实体生成中排除自定义属性表,只需编写自己的自定义数据访问代码处理它。
我错过了任何其他选择吗?有没有办法让SQL服务器“查看”列中的XML数据,这样它现在可以实际使用选项4做什么?
答案 0 :(得分:3)
我在这种情况下使用xml类型列...
http://msdn.microsoft.com/en-us/library/ms189887.aspx
在xml之前我们必须使用选项3.在我看来,这仍然是一个很好的方法。如果你有一个能够为你正确处理类型转换的数据访问层,那就大概相当了。我们将所有内容存储为字符串值,并定义了一个包含转换的orignial数据类型的列。
选项1和2是禁止的。请勿动态更改生产中的数据库架构。
选项5可以在一个单独的数据库中完成......但是仍然无法控制模式,用户需要创建表等的权限。
答案 1 :(得分:2)
绝对是3。
如果你有充分的理由,有时会这样做。
不要动态修改数据库结构以适应传入数据。有一天,某些东西可能会破坏并损坏您的数这不是这样做的。
答案 2 :(得分:2)
我认为只有3或4个 - 你不想动态改变架构,特别是如果你正在使用某种映射层。
我通常使用选项3.作为一点理智,我总是在CUSTOM_PROPERTY表中有一个类型列,它在CUSTOM_PROPERTY_VALUE表中重复。通过在< Primary Key,Type>的CUSTOM_PROPERTY表中添加一个超级键,您可以拥有一个引用它的外键(以及只有主键的简单外键)。最后,检查约束确保只有CUSTOM_PROPERTY_VALUE中的相关列不为null,基于此类型列。
通过这种方式,您知道如果某人定义了类型为int的CUSTOM_PROPERTY(例如,Tine计数),那么您实际上只会找到存储在CUSTOM_PROPERTY_VALUE表中的int,对于此属性的所有实例
修改的
如果您需要它来引用多个实体表,那么它可能会变得更复杂,尤其是如果您想要完整的参照完整性。例如(数据库中有两个不同的实体类型):
create table dbo.Entities (
EntityID uniqueidentifier not null,
EntityType varchar(10) not null,
constraint PK_Entities PRIMARY KEY (EntityID),
constraint CK_Entities_KnownTypes CHECK (
EntityType in ('Foo','Bar')),
constraint UQ_Entities_KnownTypes UNIQUE (EntityID,EntityType)
)
go
create table dbo.Foos (
EntityID uniqueidentifier not null,
EntityType as CAST('Foo' as varchar(10)) persisted,
FooFixedProperty1 int not null,
FooFixedProperty2 varchar(150) not null,
constraint PK_Foos PRIMARY KEY (EntityID),
constraint FK_Foos_Entities FOREIGN KEY (EntityID) references dbo.Entities (EntityID) on delete cascade,
constraint FK_Foos_Entities_Type FOREIGN KEY (EntityID,EntityType) references dbo.Entities (EntityID,EntityType)
)
go
create table dbo.Bars (
EntityID uniqueidentifier not null,
EntityType as CAST('Bar' as varchar(10)) persisted,
BarFixedProperty1 float not null,
BarFixedProperty2 int not null,
constraint PK_Bars PRIMARY KEY (EntityID),
constraint FK_Bars_Entities FOREIGN KEY (EntityID) references dbo.Entities (EntityID) on delete cascade,
constraint FK_Bars_Entities_Type FOREIGN KEY (EntityID,EntityType) references dbo.Entities (EntityID,EntityType)
)
go
create table dbo.ExtendedProperties (
PropertyID uniqueidentifier not null,
PropertyName varchar(100) not null,
PropertyType int not null,
constraint PK_ExtendedProperties PRIMARY KEY (PropertyID),
constraint CK_ExtendedProperties CHECK (
PropertyType between 1 and 4), --Or make type a varchar, and change check to IN('int', 'float'), etc
constraint UQ_ExtendedProperty_Names UNIQUE (PropertyName),
constraint UQ_ExtendedProperties_Types UNIQUE (PropertyID,PropertyType)
)
go
create table dbo.PropertyValues (
EntityID uniqueidentifier not null,
PropertyID uniqueidentifier not null,
PropertyType int not null,
IntValue int null,
FloatValue float null,
DecimalValue decimal(15,2) null,
CharValue varchar(max) null,
EntityType varchar(10) not null,
constraint PK_PropertyValues PRIMARY KEY (EntityID,PropertyID),
constraint FK_PropertyValues_ExtendedProperties FOREIGN KEY (PropertyID) references dbo.ExtendedProperties (PropertyID) on delete cascade,
constraint FK_PropertyValues_ExtendedProperty_Types FOREIGN KEY (PropertyID,PropertyType) references dbo.ExtendedProperties (PropertyID,PropertyType),
constraint FK_PropertyValues_Entities FOREIGN KEY (EntityID) references dbo.Entities (EntityID) on delete cascade,
constraint FK_PropertyValues_Entitiy_Types FOREIGN KEY (EntityID,EntityType) references dbo.Entities (EntityID,EntityType),
constraint CK_PropertyValues_OfType CHECK (
(IntValue is null or PropertyType = 1) and
(FloatValue is null or PropertyType = 2) and
(DecimalValue is null or PropertyType = 3) and
(CharValue is null or PropertyType = 4)),
--Shoot for bonus points
FooID as CASE WHEN EntityType='Foo' THEN EntityID END persisted,
constraint FK_PropertyValues_Foos FOREIGN KEY (FooID) references dbo.Foos (EntityID),
BarID as CASE WHEN EntityType='Bar' THEN EntityID END persisted,
constraint FK_PropertyValues_Bars FOREIGN KEY (BarID) references dbo.Bars (EntityID)
)
go
--Now we wrap up inserts into the Foos, Bars and PropertyValues tables as either Stored Procs, or instead of triggers
--To get the proper additional columns and/or base tables populated
答案 3 :(得分:0)
我倾向于将数据存储为XML,如果数据库支持得很好,或者为不同的数据类型设置少量不同的表(尝试格式化数据以使其适合少数类型之一 - 不要将一个表用于VARCHAR(15),将另一个表用于VARCHAR(20)等。)像#5这样的东西,但是所有表都是预先创建的,并且所有内容都在现有表中。每行应包含主记录ID,记录类型指示符和一段数据。设置基于记录类型的索引,按数据子索引,并且可以查询特定的字段值(其中RecType == 19和Data =='Fred')。查询匹配多个字段值的记录会更难,但这就是生命。