首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >具有多种产品类别的网站数据库设计

具有多种产品类别的网站数据库设计
EN

Stack Overflow用户
提问于 2019-02-20 03:19:42
回答 3查看 3.1K关注 0票数 1

我是新加入SQL的。试着尽可能多地学习,所以我把小网店作为我的训练目标。我在数据库结构上苦苦挣扎。我想要实现的是:

  • 有ID和名称的类别(例如电视、洗衣机)
  • 字段(每个类别的字段仅限于该类别(例如电视分辨率、HDR等)、垫圈容量、洗涤周期)
  • 产品(每个产品应该有一般的领域(名称,品牌等)和额外的类别字段,将在每个类别中不同。

因此,系统方面的主要思想是创建一个类别,向其中添加字段,并使用general +类别字段将一些产品插入到该类别中。

我怎样才能做到这一点?我试着把这一切和一对多的关系联系起来,但这似乎不像我所期望的那样有效。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-02-20 04:00:52

这是一个已知的(反)模式,称为“实体属性值”(如果您想知道更多信息,可以在internet上搜索该名称)。

现在(尤其是Postgres),我会选择一个JSONB列,该列存储每个产品的类别特定属性,而不是一个额外的fields表。

您甚至可以根据product表中的元信息验证category表中的动态属性。

所以就像这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
create table category
(
   id integer primary key, 
   name varchar(50) not null,
   allowed_attributes jsonb not null
);

create table product
(
   id integer primary key, 
   name varchar(100) not null, 
   brand varchar(100) not null, -- that should probably be a foreign key
   ... other common columns ...
);

create table product_category
(
   product_id integer not null references product,
   category_id integer not null references category, 
   attributes jsonb not null, -- category specific attributes
   primary key (product_id, category_id)
);

现在,使用类别表中的“允许属性”列表,我们可以编写一个触发器来验证它们。

首先,我创建了一个小助手函数,它确保来自一个JSON值的所有键都存在于另一个JSON值中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
create function validate_attributes(p_allowed jsonb, p_to_check jsonb)
  returns boolean
as
$$
   select p_allowed ?& (select array_agg(k) from jsonb_object_keys(p_to_check) as t(k));
$$
language sql;

然后,在类别表的触发器中使用此函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
create function validate_category_trg()
  returns trigger
as
$$
declare
   l_allowed jsonb;
   l_valid   boolean;
begin

   select allowed_attributes 
      into l_allowed
   from category
   where id = new.category_id;

   l_valid := validate_attributes(l_allowed, new.attributes);
   if l_valid = false then 
     raise 'some attributes are not allowed for that category';
   end if;
   return new;
end;
$$
language plpgsql;

现在,让我们插入一些示例数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
insert into category (id, name, allowed_attributes)
values
(1, 'TV Set', '{"display_size": "number", "color": "string"}'::jsonb), 
(2, 'Laptop', '{"ram_gb": "number", "display_size": "number"}');

insert into product (id, name)
values
(1, 'Big TV'),
(2, 'Small  TV'),
(3, 'High-End Laptop');

现在让我们插入类别信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
insert into product_category (product_id, category_id, attributes)
values
(1, 1, '{"display_size": 60}'),  -- Big TV 
(2, 1, '{"display_size": 32}'),  -- Small TV
(3, 2, '{"ram_gb": 128}'); -- Laptop

这是因为所有属性都是在类别中定义的。如果我们试图插入以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
insert into product_category (product_id, category_id, attributes)
values
(3, 2, '{"usb_ports": 5}');

然后,触发器将抛出一个异常,防止使用插入行。

这可以扩展到实际使用存储在allowed_attributes中的数据类型信息。

要根据属性查找产品,我们可以使用Postgres提供的JSON函数,例如所有具有display_size的产品:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select p.*
from product p
where exists (select *
              from product_category pc
              where pc.product_id = p.id 
                and pc.attributes ? 'display_size');

找到包含多个属性的产品同样容易(而且使用“传统”EAV模型要复杂得多)。

下面的查询只查找具有属性display_size ram_gb的产品

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select p.*
from product p
where exists (select *
              from product_category pc
              where pc.product_id = p.id 
                and pc.attributes ?& '{display_size, ram_gb}');

这可以非常有效地进行索引,使搜索速度更快。

我不完全确定您是否想要将属性存储在product_category表中。也许它们应该直接存储在product表中-但这取决于您的需求和您想要如何管理它们。

使用上述方法,您可以拥有一个类别"Computer“来存储诸如CPU数量、RAM和时钟速度等信息。这个类别(及其属性)可以同时使用,例如智能手机和笔记本电脑。

但是,如果要这样做,则需要product_category中的多行来全面描述产品。

最常见的方法可能是直接将属性存储在产品上,并跳过所有的动态JSONB验证。

所以就像这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
create table category
(
   id integer primary key, 
   name varchar(50) not null
);

create table product
(
   id integer primary key, 
   name varchar(100) not null, 
   brand varchar(100) not null, -- that should probably be a foreign key
   attributes jsonb not null, 
   ... other common columns ...
);

create table product_category
(
   product_id integer not null references product,
   category_id integer not null references category, 
   primary key (product_id, category_id)
);

或者,如果您需要类别特定的动态属性、产品特定属性,而不考虑类别,甚至两者的组合。

票数 4
EN

Stack Overflow用户

发布于 2019-02-20 03:32:57

您可以创建连接表和外键来表示表之间的关系。

类别表

id _名称

字段表

id _名称

类别字段表

id = category_id \ field_id

品牌 id = name

产品表

id \ category_id _ brand_id _

产品特征

id = product_id \ field_id \x=

对于Products表,您可能需要考虑为品牌拥有一个单独的表,并在products表中使用brand_id列而不是名称,以避免重复。

category_fields表将存储类别的id和相关字段的id,表中的每一行代表该类别的不同字段。

然后,表product_features将存储特征,这将取决于分配给产品类别的字段。

票数 0
EN

Stack Overflow用户

发布于 2019-02-21 06:43:38

使用基于数据的伪码、内联引用(外键)、数据类型和不相关的详细信息省略:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
create table Category {
  CategoryId,
  CategoryName,
  key { CategoryId },
  key { CategoryName } /* Don't want categories that differ only by surrogate id */
};

/* Allowed fields */
create table CategoryField {
  CategoryId,
  FieldName,
  key { CategoryId, FieldName },
  reference CategoryField_Category
    { CategoryId } references Category { CategoryId }
};

create table Product {
  ProductId,
  ProductName,
  ProductBrand,
  CategoryId,
  key { ProductId }, /* Probably other attributes, keys and references as well */
  reference Product_Category
    { CategoryId } references Category { CategoryId }
};

create table ProductFieldValue {
  ProductId,
  CategoryId, /* Violates BCNF, but is controlled by foreign superkey */
  FieldName,
  FieldValue,
  key { ProductId, FieldName },
  reference PFV_Product
    { ProductId, CategoryId } references Product { ProductId, CategoryId },
  reference PFV_CategoryField
    { CategoryId, FieldName } references CategoryField { CategoryId, FieldName }
};

重叠外键(我更喜欢“引用”一词,特别是因为其中一个实际上是正确的外接超级键)确保每个产品只能根据CategoryField表中的行具有字段值。

在这个模型中有一些冗余--ProductFieldValue违反了Boyce-Codd范式(2NF,但没关系)-so,如果简单的完整性控制的好处超过了这个缺点,您将不得不自己决定。但是请注意,冗余是被控制的;不会有任何不一致。

该模型假设所有字段值都具有相同的数据类型,例如字符串。如果您也想要约束(例如,一些字段只能有数值;有些字段是枚举的等等),那么事情就会变得更加复杂。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54785101

复制
相关文章
为什么这段代码输出的是”Hello World”
Stackoverlfow.com上有一篇有趣的讨论帖: 在这篇帖子里提到了如下的程序: 明明是在程序里使用了java.util.Ramdom()函数产生随机数,为什么每次打出的结果都是Hello w
哲洛不闹
2018/09/14
9910
为什么这段代码输出的是”Hello World”
MSDN官方的ASP.Net异步页面的经典示例代码
示例1.演示异步获取一个网址的内容,处理后显示在OutPut这一Label上 using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Net; using System.IO; using System.Text; using System.Text.RegularExpressions; public partial class AsyncPag
菩提树下的杨过
2018/01/24
1K0
为什么这段代码输出的是”Hello World”
Stackoverlfow.com上有一篇有趣的讨论帖: 在这篇帖子里提到了如下的程序: 明明是在程序里使用了java.util.Ramdom()函数产生随机数,为什么每次打出的结果都是Hello w
哲洛不闹
2018/09/19
1K0
为什么这段代码输出的是”Hello World”
代码审查|这段代码,为什么复制文件夹总是“成功”?
最近开始一个人负责整个项目的全栈开发和维护,工作中没了和同事交叉 code review 的环节,所以就打算,如果工作中遇到一些比较典型的代码,包括好味道和坏味道,就拿出来分析下,与大家一起交流,作为另一种形式的「交叉 review」。
mzlogin
2023/10/23
1220
理解项目代码,我做了什么
测试人员如何快速熟悉项目代码,是一个比较头疼的问题,作为测试架构师,有义务去解决这个问题,最近在为团队赋能的时候,做了三件事,来帮助团队成员快速熟悉业务代码,方便后期更好的做测试策略设计。
CKL的思考
2023/02/01
3270
谷歌工程师写的这段代码什么水平?
代码,正在改变世界。正是因为有了代码的存在,才有了阿里巴巴、百度、京东、天猫等的存在。
顶级程序员
2018/07/23
5680
谷歌工程师写的这段代码什么水平?
这段代码很有意思!
作为菜鸟的我,在面试的时候,看到了这阴间代码,心里想着谁无聊写这样的代码呀,屁点用没有,却只能默默的分析题目,但最终还是错了几个,回来之后又好好分析了一下,其实也不是很难,只是在面试的时候看到这样的东西,实在是有点搞心态了!
小丞同学
2021/08/16
5080
JVM优化Java代码时都做了什么?
JVM 在对代码执行的优化可分为运行时(runtime)优化和即时编译器(JIT)优化。运行时优化主要是解释执行和动态编译通用的一些机制,比如说锁机制(如偏斜锁)、内存分配机制(如 TLAB)等。除此之外,还有一些专门用于优化解释执行效率的,比如说模版解释器、内联缓存(inline cache,用于优化虚方法调用的动态绑定)。
本人秃顶程序员
2019/05/28
1.6K0
JVM优化Java代码时都做了什么?
JVM优化Java代码时都做了什么?
JVM 在对代码执行的优化可分为运行时(runtime)优化和即时编译器(JIT)优化。
葆宁
2019/04/18
6290
看完python这段爬虫代码,java流
如果不能正确安装,请检查你的环境变量,至于环境变量配置,在这里不再赘述,相关文章有很多。
py3study
2020/01/22
7030
JVM优化Java代码时都做了什么?
JVM优化Java代码时都做了什么? JVM在对代码执行的优化可分为运行时化和即时编译器优化。运行时优化主要是解析执行和动态编译通用的一些机制,比如说锁机制(如偏向锁)、内存分配机制(如TLAB)。除
葆宁
2019/04/18
4410
new 做了什么?
大部分讲 new 的文章会从面向对象的思路讲起,但是我始终认为,在解释一个事物的时候,不应该引入另一个更复杂的事物。
小小范
2022/01/20
3790
刚写完这段代码,就被开除了……
显然不是,休眠的逻辑,大家都懂,不需要写注释,你注释写休眠 1 天也没意义啊。。。
Java技术栈
2018/12/11
6770
刚写完这段代码,就被开除了……
中级JAVA:JVM优化Java代码时都做了什么?
JVM 在对代码执行的优化可分为运行时(runtime)优化和即时编译器(JIT)优化。
葆宁
2022/01/06
2060
MSDN 杂志 Windows 8 特刊
Windows 运行时: 使用 Windows 运行时改变应用程序开发的面貌 Windows 运行时是创建 Windows 应用商店应用程序的核心。 了解它如何为创建这些新型应用程序提供强大平台,并附带功能丰富且设计精心一致的开发图面。 Jason Olson Windows 运行时和 CLR: 深入了解 .NET 和 Windows 运行时 综观 CLR 如何使托管开发者与 Windows 运行时交互,公开 Windows 运行时 API,就像它们只是可供 .NET 开发人员使用的另一个托管类库一样。
张善友
2018/01/30
1.1K0
MSDN 杂志 Windows 8 特刊
为什么学了那么多门语言,我还是编不好这段代码
引言:本文节选自朱赟博士在极客时间App开设的“朱赟的技术管理课”,已获授权。更多精彩文章,可以点击文末“阅读原文”免费试读。
Java架构师必看
2021/09/26
3560
链接器做了什么?
摘自《程序员自我修养》 ---- 机器指令的历史 为了更好的理解计算机程序的编译和链接的过程,我们简单地回顾计算机程序开发的历史一定会非常有益。 最原始的设备是就是纸带,即在纸带上打相应的孔。 这个过程我们可以通过图2-6来看到,假设有一种计算机,它的每条指令是一个字节,也就是8位。它的高4位是0001,表示这是一种跳转指令,低4位存放的是跳转目的地的绝对地址。我们可以从图2-6中看到,这个程序的第一条指令就是一条跳转指令,它的目的地址是第5条指令(注意,第5条指令的绝对地址是4).至于0和1怎么映射到
233333
2018/04/13
9900
链接器做了什么?
select语句做了什么?
爱撒谎的男孩
2023/08/25
1250
JS中级测试: 这段代码最终会输出多少?​
可以看到的是 return this 的结果是 WINDOW对象,而不是 obj;
舒克
2019/08/19
1.7K0
点击加载更多

相似问题

这段代码做了什么

70

这段JavaScript代码做了什么?

61

这段perl代码做了什么?

10

这段xkcd代码做了什么?

10

这段javascript代码做了什么?

10
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文