mal*_*yeb 7 mysql database-design
我正在为新的基于 PHP/MySql 的应用程序设计数据库。
我的问题是我没有也不能代表应该保存在数据库中的内容,因为它是无限的并且不断变化。
这是问题示例:该应用程序将是一个购物网站,其中包含多种产品,所有产品都具有一些共享属性,例如title和,price但有些类型具有特定的详细信息,例如expiry date有些具有isbn一些非。
这只是一个例子,但我真的有很多不同属性的种类。
我可以为每个种类创建一个表格,但我拥有的并不是所有可用的种类,目前还有很多种类的项目是未知的。
他们是一种解决这个问题的方法吗?
Chr*_*ers 10
未知种类的数据对我来说听起来有些可疑。当然,你的例子都是众所周知的。对于商品和服务,仔细分析和规范化很重要,我认为您可以摆脱核心数据的 EAV 建模(我认为这会导致比解决的问题更多)。其余的可以填充在 XML 字段等中。此外,如果您的设计正确,您始终可以适当地扩展信息。考虑以下三个表:
CREATE TABLE products (
id int autoincrement primary key,
sellprice numeric,
part_code varchar(10),
title varchar(32),
description text
);
CREATE TABLE barcode_type (
id int autoincrement primary key,
label varchar(15) not null unique
);
CREATE TABLE make_model (
id int autoincrement primary key,
make varchar(15) not null,
model varchar(15),
barcode_type int references barcode_type(id),
barcode varchar(32)
);
Run Code Online (Sandbox Code Playgroud)
现在有了这个,您可以将条码(包括 ISBN、EAN、UPC 等)分配给各个部分,每个品牌/型号组合一个。如果您需要支持更多条码类型,这不难添加。至于到期日期,这些去哪里取决于你在哪里跟踪它们。如果你想有临时定价,或者为一组客户定价,你也可以添加它。
但是,您所描述的内容听起来并不是很结构化。我建议从最小的设计开始,然后根据需要进行扩展,而不是 EAV 设计,然后再后悔。
这里有一些 SQL Server 的细节,但我总体上介绍了 EAV。它并不是人们常说的魔鬼,一些典型的借口问题是可以避免的。例如,@KookieMonster 说你不能强制用户没有两个生日,但这很容易:
CREATE TABLE dbo.Users
(
UserID INT PRIMARY KEY,
Username NVARCHAR(255) UNIQUE
--, ...
);
CREATE TABLE dbo.Properties
(
PropertyID INT PRIMARY KEY,
Name SYSNAME UNIQUE
--, ...
);
CREATE TABLE dbo.UserProperties
(
UserID INT FOREIGN KEY ...,
PropertyID INT FOREIGN KEY ...,
DateValue DATE,
IntValue INT,
-- ...
PRIMARY KEY(UserID, PropertyID)
);
Run Code Online (Sandbox Code Playgroud)
(同样,这是 SQL Server 语法,但希望这个概念能引起共鸣。)
如果逻辑比这更复杂(例如,它们可以有三个电话号码但只有一个生日),那么它会变得更加复杂,但您仍然可以使用触发器、存储过程等强制执行与您的业务逻辑相匹配的事情。我不知道任何其他解决方案将如何更好地解决这个问题,同时不引入其他解决方案。
性能可能是一个问题,但是我们在 SQL Server 2008+ 中使用过滤索引(针对特定属性)和非规范化表的惰性物化解决了这个问题。对于缓慢变化的属性集,很容易进行后台处理,使表变平,以便对于某些或所有产品,您拥有数据的物化、透视版本以避免所有连接。这在 MySQL 中是如何工作的我不太确定,所以我不会提供语法,但也许我会从 SQL Server 的角度进一步讨论这个问题......
不要使用MySQL,关系型数据库不用于解决此类问题。在 Windows 上使用文档或 NoSQL 数据库,例如 MongoDB 或可能的 RavenDB。
或者也可以使用 PostgreSQL。如果您有一组基本属性,则可以将继承构建到您的表中
create table base_items
( id bigint,
title varchar(50),
price money)
Run Code Online (Sandbox Code Playgroud)
然后对于其他物品,比如书或食物
create table book_items
(isbn varchar(20))
inherits (base_items)
create table food_items (date expiry_date)
inherits(base_items)
Run Code Online (Sandbox Code Playgroud)
让你的数据
insert into base_items (id,item,amount) values
(3,'soap',0.99);
insert into food_items (id,item,expiry,amount) values
(4,'banana','2012-01-01',0.50);
insert into book_items (id,item,isbn,amount) values
(1,'some book','ABC-000-02100',20.99);
insert into book_items (id,item,isbn,amount) values
(2,'some other book','ABC-000-02102',20.99);
Run Code Online (Sandbox Code Playgroud)
和
select * from base_items;
id | item | amount
----+-----------------+--------
3 | soap | £0.99
1 | some book | £20.99
2 | some other book | £20.99
4 | banana | £0.50
select * from book_items;
id | item | amount | isbn
----+-----------------+--------+---------------
1 | some book | £20.99 | ABC-000-02100
2 | some other book | £20.99 | ABC-000-02102
select * from food_items;
id | item | amount | expiry
----+--------+--------+------------
4 | banana | £0.50 | 2012-01-01
Run Code Online (Sandbox Code Playgroud)
如果该数据库以任何方式与您的消费者所购买的产品相关联,那么性能很快就会成为您最重要的问题之一。我并不是说 EAV 在数据库世界中没有立足之地,但您可能会带来比此模型更多的问题的答案。由于我必须自己管理一个这样的(第 3 方)数据库,因此需要记住以下几点:
性能会很快变差:如果您想检索给定产品、客户的所有字段……您必须乘以 LEFT JOIN,因为每个属性都将存储在不同的行中。现在想象一下,当您有数百个字段要加入时。
数据完整性:很难执行。例如,没有人阻止客户有两个(或更多)出生日期。如果书籍需要 isbn,您将如何确保它是?你的生日字段的字段类型是什么?您可以有很多代码来帮助您解决这个问题,但是编写起来既困难又漫长,并且肯定会影响性能。
这个列表可以继续下去,我最喜欢的关于这个主题的读物是Bill Karwin 的Pragmatic SQL Antipatterns。您还可以观看此视频,不到 20 分钟的 SQL 最佳实践。我们的供应商现在无法改变他们的架构(需要几个月的重新设计),而且我们的数据量问题正在堆积如山。在走这条路之前,仔细权衡利弊。