如果您想避免在数据库设计中存储 JSON 或 XML 数据结构,这里有另一个设计建议可以让我了解规范化的想法。
Fiddle 看看下面的所有事情。
餐桌设计
这个设计不完整!查看底部附近的未来行动。
-- item configuration data
create table ItemType -- define item types
(
TypeId int,
TypeName nvarchar(100)
);
create table PropType -- define property types
(
PropId int,
PropName nvarchar(100)
);
create table ItemTypeProp -- link property types to item types
(
TypeId int,
PropId int
);
-- transactional item data
create table Item -- links item to type
(
ItemId int,
ItemName nvarchar(100),
TypeId int
);
create table ItemPropString -- item string property values
(
ItemId int,
PropId int,
PropValue nvarchar(100) --string...
);
create table ItemPropInt -- item integer property values
(
ItemId int,
PropId int,
PropValue int --integer...
);
create table ItemPropDate -- item date property values
(
ItemId int,
PropId int,
PropValue date --date...
);
示例配置数据
-- independent item types
insert into ItemType (TypeId, TypeName) values
(1, 'Fridge'),
(2, 'Moon');
-- independent property types
insert into PropType (PropId, PropName) values
(1, 'Height'),
(2, 'Width'),
(3, 'PurchaseDate'),
(4, 'Color');
-- assign properties to items (NO, you cannot purchase the items of type Moon)
insert into ItemTypeProp (TypeId, PropId) values
(1, 1), -- Fridge > Height
(1, 2), -- Fridge > Width
(1, 3), -- Fridge > PurchaseDate
(1, 4), -- Fridge > Color
(2, 2), -- Moon > Width
(2, 4); -- Moon > Color
交易数据示例
insert into Item (ItemId, ItemName, TypeId) values
(1, 'The Blue Fridge', 1),
(2, 'Your Fridge', 1),
(3, 'Mimas', 2);
insert into ItemPropString (ItemId, PropId, PropValue) values
(1, 4, 'Blue'),
(3, 4, 'Gray');
insert into ItemPropInt (ItemId, PropId, PropValue) values
(1, 1, 200),
(3, 2, 396);
insert into ItemPropDate (ItemId, PropId, PropValue) values
(1, 3, '2020-01-01');
示例查询
-- all fridges purchased in 2020
select i.ItemId, i.ItemName, it.TypeName, pt.PropName, ipd.PropValue
from Item i
join ItemType it
on it.TypeId = i.TypeId
join ItemTypeProp itp
on itp.TypeId = it.TypeId
join PropType pt
on pt.PropId = itp.PropId
join ItemPropDate ipd
on ipd.ItemId = i.ItemId
and ipd.PropId = pt.PropId
where it.TypeName = 'Fridge'
and pt.PropName = 'PurchaseDate'
and year(ipd.PropValue) = 2020;
-- all item types with their possible properties
select it.TypeId, it.TypeName, pt.PropId, pt.PropName
from ItemType it
join ItemTypeProp itp
on itp.TypeId = it.TypeId
join PropType pt
on pt.PropId = itp.PropId;
-- The Blue Fridge with all its possible properties and available values
select i.ItemId,
i.ItemName,
it.TypeName,
pt.PropName,
coalesce(ips.PropValue,
convert(nvarchar(100), ipi.PropValue),
convert(nvarchar(100), ipd.PropValue)) as PropValue
from Item i
join ItemType it
on it.TypeId = i.TypeId
join ItemTypeProp itp
on itp.TypeId = it.TypeId
join PropType pt
on pt.PropId = itp.PropId
left join ItemPropString ips
on ips.PropId = pt.PropId
and ips.ItemId = i.ItemId
left join ItemPropInt ipi
on ipi.PropId = pt.PropId
and ipi.ItemId = i.ItemId
left join ItemPropDate ipd
on ipd.PropId = pt.PropId
and ipd.ItemId = i.ItemId
where i.ItemName = 'The Blue Fridge';
-- The Blue Fridge with all its possible properties and available values PIVOTED
with cte as
(
select i.ItemId,
i.ItemName,
it.TypeName,
pt.PropName,
coalesce(ips.PropValue,
convert(nvarchar(100), ipi.PropValue),
convert(nvarchar(100), ipd.PropValue)) as PropValue
from Item i
join ItemType it
on it.TypeId = i.TypeId
join ItemTypeProp itp
on itp.TypeId = it.TypeId
join PropType pt
on pt.PropId = itp.PropId
left join ItemPropString ips
on ips.PropId = pt.PropId
and ips.ItemId = i.ItemId
left join ItemPropInt ipi
on ipi.PropId = pt.PropId
and ipi.ItemId = i.ItemId
left join ItemPropDate ipd
on ipd.PropId = pt.PropId
and ipd.ItemId = i.ItemId
where i.ItemName = 'The Blue Fridge'
)
select p.*
from cte
pivot (max(cte.PropValue) for PropName in ([Height], [Width], [PurchaseDate], [Color])) p
备注
正面:
- 标准化:没有值被保存两次
- 非常可扩展的设计,例如:一个项目类型可以具有从某个日期开始的某个属性(添加列
ItemTypeProp.FromDate)
- 无需将所有属性值存储为字符串值(这意味着您可以使用特定于数据类型的函数,例如
year())
- 无需在查询中处理 JSON 或 XML 函数和路径表达式
未来的行动点:
-
ItemPropInt 需要一个 UnitId 列(土星的卫星土卫一是 396 KM 宽,而蓝色冰箱是 200 CM 高)
- 添加约束以避免为未链接到项目类型的属性类型添加属性值
- 在正确的 Id 和 Name 字段上添加索引以加快查询速度