ver*_*ism 12 mysql database-design
我正在设计一个数据库来存储不同类型的用户。主要(但不完全)他们将是演员、导演和作家。目前只有四种相关的用户类型。这个数字有增加的可能性,但概率很低——在这种情况下,这个数字会非常小。
该计划是有一个users
几乎完全负责登录站点的表(name
,email
和password
列加上一两个其他的,例如它们是否已被批准,和 update_at),以及每个用户类型的附加表,每个用户类型有自己独特的一组列。例如,只有演员才有种族栏,只有导演才有简历栏,只有作家需要提供他们的位置。但是,由于我之前没有管理过如此复杂的数据库,我想知道如何组织几个方面:
首先,用户可以是上述类型中的任意一种或任意组合。所以我知道我需要像(例如)一个director_user
带有director_id
和user_id
列的表格。那么这是否足以能够按角色类型等过滤所有用户?
其次,大多数用户会选择 Twitter 个人资料和电话号码。并且所有演员都必须至少包含一个 URL,用于其他任何在线演员的个人资料;目前他们可以包括三个,但这个数字可能会增加。我是否正确假设每个可能的配置文件/联系方法的单独表格是组织数据的最佳方式?
MDC*_*CCL 24
根据我对您对相关业务上下文的描述的解释,您正在处理超类型子类型1结构,其中 (a) Actor、Director和Writer是 (b) Person的实体子类型,它们的实体超类型,以及 (c) 表示亚型不是相互排斥。
通过这种方式,如果您有兴趣构建一个准确反映此类场景的关系数据库(因此期望它具有这样的功能),那么您的以下评论澄清对于前面的几点非常重要,因为它们对所讨论的数据库的(1)概念和 (2)逻辑表示级别:
- [...] 每个用户类型的附加表,每个用户类型都有自己独特的一组列。
- [...] 只有四种相关的用户类型。这个数字有增加的可能性,但概率很低——在这种情况下,这个数字会非常小。
我将在下面的部分中详细说明所有这些方面和其他几个关键因素。
为了首先定义相应的概念模式——它可以用作后续参考,以便您可以对其进行调整以确保它满足确切的信息要求——,我制定了一些特别重要的业务规则:
然后,我创建了图 1 中所示的 IDEF1X 4图,其中将上述所有公式与其他相关规则组合在一起:
正如所演示的,Person超类型 (i) 有自己的盒子,(ii) 拥有适用于所有子类型,以及 (iii) 显示将它与每个子类型的框连接起来的线条。
反过来,每个子类型 (a) 出现在其自己的专用框中,并且 (b) 仅保留其适用的属性。超类型PersonId的 PRIMARY KEY将5迁移到角色名称分别为6 ActorId、DirectorId和WriterId的子类型。
此外,我避免将Person与UserProfile实体类型耦合,这允许分离它们的所有上下文含义、关联或关系等。PersonId属性已迁移到角色名称为UserId 的UserProfile。
您在问题正文中声明
并且所有演员都必须至少包含一个 URL,用于其他任何在线演员的个人资料;目前他们可以包括三个,但这个数字可能会增加。
...所以URL本身就是一种实体类型,根据此引用直接与Actor子类型相关联。
并且,在comments 中,您指定
[...] 演员会有爆头(照片),而作家不会 [...]
…然后,除其他功能外,我还包括Headshot作为Actor实体类型的属性。
至于Ethnicity 和Location实体类型,它们当然可能涉及更复杂的组织(例如,一个Actor可能以不同的比例属于一个、两个或多个不同的族群,而一个Writer可能基于需要记录的地方国家、行政区、县等),但看起来您的业务环境的需求已被此处建模的结构成功覆盖。
当然,您可以根据需要进行尽可能多的调整。
因此,基于上面显示和描述的 IDEF1X 图,我编写了如下所示的逻辑 DDL 布局(我提供了注释作为注释,解释了我认为在表、列和约束方面特别重要的一些特征声明):
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient INDEX strategies based on the exact
-- data manipulation tendencies of your business needs.
-- As one would expect, you are free to utilize
-- your preferred (or required) naming conventions.
CREATE TABLE Person ( -- Represents the supertype.
PersonId INT NOT NULL,
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
BirthDate DATE NOT NULL,
GenderCode CHAR(3) NOT NULL,
TwitterProfile CHAR(30) NOT NULL,
PhoneNumber CHAR(30) NOT NULL,
EmailAddress CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Person_PK PRIMARY KEY (PersonId),
CONSTRAINT Person_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
FirstName,
LastName,
GenderCode,
BirthDate
),
CONSTRAINT Person_AK2 UNIQUE (TwitterProfile), -- ALTERNATE KEY.
CONSTRAINT Person_AK3 UNIQUE (EmailAddress) -- ALTERNATE KEY.
);
CREATE TABLE Ethnicity ( -- Its rows will serve a “look-up” purpose.
EthnicityId INT NOT NULL,
Name CHAR(30) NOT NULL,
Description CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Ethnicity_PK PRIMARY KEY (EthnicityId),
CONSTRAINT Ethnicity_AK UNIQUE (Description)
);
CREATE TABLE Actor ( -- Stands for one of the subtypes.
ActorId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Headshot CHAR(30) NOT NULL, -- May, e.g., contain a URL indicating the path where the photo file is actually stored.
EthnicityId INT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Actor_PK PRIMARY KEY (ActorId),
CONSTRAINT ActorToPerson_FK FOREIGN KEY (ActorId)
REFERENCES Person (PersonId),
CONSTRAINT ActorToEthnicity_FK FOREIGN KEY (EthnicityId)
REFERENCES Ethnicity (EthnicityId)
);
CREATE TABLE Director ( -- Denotes one of the subtypes
DirectorId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Bio CHAR(120) NOT NULL,
Etcetera CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Director_PK PRIMARY KEY (DirectorId),
CONSTRAINT DirectorToPerson_FK FOREIGN KEY (DirectorId)
REFERENCES Person (PersonId)
);
CREATE TABLE Country (
CountryCode CHAR(2) NOT NULL,
Name CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Country_PK PRIMARY KEY (CountryCode),
CONSTRAINT Country_AK UNIQUE (Name)
);
CREATE TABLE Location ( -- Its rows will serve a “look-up” purpose.
CountryCode CHAR(2) NOT NULL,
LocationCode CHAR(3) NOT NULL,
Name CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Location_PK PRIMARY KEY (CountryCode, LocationCode),
CONSTRAINT Location_AK UNIQUE (CountryCode, Name)
);
CREATE TABLE Writer ( -- Represents one of the subtypes.
WriterId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
CountryCode CHAR(2) NOT NULL,
LocationCode CHAR(3) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Writer_PK PRIMARY KEY (WriterId),
CONSTRAINT WriterToPerson_FK FOREIGN KEY (WriterId)
REFERENCES Person (PersonId),
CONSTRAINT WriterToLocation_FK FOREIGN KEY (CountryCode, LocationCode)
REFERENCES Location (CountryCode, LocationCode)
);
CREATE TABLE UserProfile (
UserId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
UserName CHAR(30) NOT NULL,
Etcetera CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK UNIQUE (UserName), -- ALTERNATE KEY.
CONSTRAINT UserProfileToPerson_FK FOREIGN KEY (UserId)
REFERENCES Person (PersonId)
);
CREATE TABLE URL (
ActorId INT NOT NULL,
Address CHAR(90) NOT NULL,
Etcetera CHAR(30) NOT NULL,
AddedDateTime DATETIME NOT NULL,
--
CONSTRAINT URL_PK PRIMARY KEY (ActorId, Address), -- Composite PRIMARY KEY.
CONSTRAINT URLtoActor_FK FOREIGN KEY (ActorId)
REFERENCES Actor (ActorId)
);
Run Code Online (Sandbox Code Playgroud)
这已经在这个在 MySQL 8.0 上运行的db<>fiddle中进行了测试。
因此,(1)逻辑布局的每一个奇异方面所携带来自一个非常精确的含义(2)感兴趣的商业环境的奇异特性7与精神-in协议关系框架由埃德加·弗兰克·科德博士- ,因为:
每个基表代表一个单独的实体类型。
每列代表各自实体类型的单个属性。
每个列都有一个特定的数据类型,以确保它包含的所有值都属于一个特定且正确分隔的集合 a ,无论是 INT、DATETIME、CHAR 等(让我们希望 MySQL 最终将 DOMAIN近期版本支持)。
配置多个约束(以声明方式)以保证所有表中保留的行形式的断言符合在概念级别确定的业务规则。
每行都旨在传达明确定义的语义,例如,Person
读取一行:
PersonId 标识的 Person 由r
FirstNames
和 LastName调用t
,出生于 BirthDate u
,具有 GenderCode v
,TwitterProfile 上的推文w
,通过 PhoneNumber 到达x
,通过 EmailAddress 联系y
,并在 CreatedDateTime 注册z
。
拥有这样的布局绝对是有利的,因为您可以派生出新的表(例如,在 JOIN 子句的帮助下从多个表中收集列的 SELECT 操作),这些表 - 连续 - 也具有非常精确的含义(请参阅标题为下面的“视图”)。
值得一提的是,使用这种配置,(i) 代表子类型实例的行由 (ii) 相同的 PRIMARY KEY 值标识,该值区分表示互补超类型出现的行。因此,现在应该注意的是
通过这种逻辑设计,如果新的子类型被定义为与您的业务上下文相关,则您必须声明一个新的基表,但是当其他类型的实体类型被认为具有重要性时也会发生这种情况,因此上述情况将是,其实,普通。
为了“获取”,例如,对应于Actor,Director或Writer 的所有信息,您可以声明一些视图(即派生表或可表达表),以便您可以直接从单个资源中进行 SELECT ,而无需编写每次都与 JOIN 有关;例如,使用下面声明的 VIEW,您可以获得“完整” Actor信息:
--
CREATE VIEW FullActor AS
SELECT P.FirstName,
P.Lastname,
P.BirthDate,
P.GenderCode,
P.TwitterProfile,
P.PhoneNumber,
P.EmailAddress,
A.Headshot,
E.Name AS Ethnicity
FROM Person P
JOIN Actor A
ON A.ActorId = P.PersonId
JOIN Ethnicity E
ON E.EthnicityId = A.EthnicityId;
--
Run Code Online (Sandbox Code Playgroud)
当然,您可以采用类似的方法来检索“完整”的导演和编剧信息:
--
CREATE VIEW FullDirector AS
SELECT P.FirstName,
P.Lastname,
P.BirthDate,
P.GenderCode,
P.TwitterProfile,
P.PhoneNumber,
P.EmailAddress,
D.Bio,
D.Etcetera
FROM Person P
JOIN Director D
ON D.DirectorId = P.PersonId;
--
CREATE VIEW FullWriter AS
SELECT P.FirstName,
P.Lastname,
P.BirthDate,
P.GenderCode,
P.TwitterProfile,
P.PhoneNumber,
P.EmailAddress,
L.Name AS Location,
C.Name AS Country
FROM Person P
JOIN Writer W
ON W.WriterId = P.PersonId
JOIN Country C
ON C.CountryCode = W.CountryCode
JOIN Location L
ON L.LocationCode = W.LocationCode;
--
Run Code Online (Sandbox Code Playgroud)
这里讨论的 DML 视图也包含在这个 MySQL 8.0 db<>fiddle 中,以便您可以“实际”查看和测试它们。
1在一些概念建模技术中,超类型-子类型关联被称为超类-子类关系。
2虽然你提到有实际上存在更多的角色,一个人可以完成的,但你发现三人都不够好商量的情况下暴露几个重要的后果。
3但是,正如您所指出的,将来Actor最终可能会提供一对多的URL。
4 对信息建模集成定义(IDEF1X)是被确立为一个非常可取的建模技术标准是由美国在1993年12月美国国家标准与技术研究院(NIST)。它基于 (a) 由数据关系模型的唯一创始人,即 EF Codd 博士撰写的早期理论著作;关于 (b)实体关系视图,由PP Chen 博士开发;以及 (c) 逻辑数据库设计技术,由 Robert G. Brown 创建。
5 IDEF1X 标准将键迁移定义为“将父实体或泛型 [即超类型] 实体的主键置于其子实体或类别实体 [即子类型] 中作为外键的建模过程”。
6在 IDEF1X 中,角色名称是分配给 FK 属性的独特标签,以表达其在其各自实体类型范围内的含义。
7当然,对于假设的概念属性(和逻辑列)Director.Etcetera和UserProfile.Etcetera除外,它们只是我用来公开添加更多适用于相应概念实体类型的属性(和列)的可能性的占位符(和逻辑表)。
8例如,将具有AUTO_INCREMENT属性的附加列附加到“运行”在 MySQL 上的数据库的表中。
您应该像这样将其拆分到表中(仅显示呈现概念所需的列,不一定是所有列):
Users
ID Username FirstName LastName PasswordHash ...
1 'Joe1' 'Joe' 'Smith'
2 'Freddy' 'Fred' 'Jones'
Roles
ID RoleType ....
1 'Writer'
2 'Director'
3 'Actor'
User_Roles
User_ID Role_ID ...
1 1
1 2
2 2
2 3
Run Code Online (Sandbox Code Playgroud)
这将为您提供一个充满用户的表以及所有各种用户列、一个角色表以及一个将两者连接在一起的链接表。
通过 User_Roles 中的条目可以看出 Joe1 既是作家又是导演。弗莱迪既是导演又是演员。
这还允许您稍后添加更多角色,而无需更改系统。只需插入制作人或编辑器或其他任何内容的记录即可。
因此,要查找演员的所有用户名,您有以下几种选择:
Select Distinct Username
from Users
Where User_ID in (select User_ID from User_Roles where Role_ID = 3)
Run Code Online (Sandbox Code Playgroud)
或者,如果您不知道 role_ID 编号,则:
Select Distinct Username
from Users
Where User_ID in (Select User_ID from User_Roles where Role_ID =
(Select ID from Roles where RoleType = 'Actor')
)
Run Code Online (Sandbox Code Playgroud)
或者你也可以这样做:
select u.Username, r.RoleType
from Users u
inner join User_Roles ur on ur.User_ID = u.ID
inner join Roles r on r.ID = ur.Role_ID
where r.RoleType = 'Actor'
Run Code Online (Sandbox Code Playgroud)
(在此版本中,您可以使用也使用Where r.Role_ID = 3
来获得相同的结果。)
但我会使用第一个查询和我知道的任何 WHERE 子句。在大型系统中,了解 Role_ID 通常比了解文本运行得更快,因为数字数据对于大多数 SQL 引擎处理索引来说“更容易”且更高效。
至于联系方式或图片或其他什么,我会以类似的方式进行:
Attributes
ID MethodText ...
1 'TwitterID'
2 'URL'
3 'CellPhone'
4 'Email'
5 'PictureLink'
Role_Attributes
Role_ID Attribute_ID isRequired
3 5 1
3 4 1
3 3 0
User_Attributes
User_ID Attribute_ID AttributeData
1 4 'Joe@Example.com'
1 1 '@joe'
1 3 '555-555-5555'
1 5 'www.example.com/pics/myFace.png'
Run Code Online (Sandbox Code Playgroud)
...等等。它们将以与用户到角色相同的方式链接。
这表明每个角色有0到多个属性,这些属性可能是可选的。那么每个用户都有 0 到多个属性,以及这些属性的数据。
这使您可以随着时间的推移添加新属性,而无需重写任何代码;只需更新 attribute 和 role_attributes 表以匹配您的新规则。它还允许您在角色之间共享属性,而无需为每个用户重新输入相同的数据。如果两个角色需要图片,那么他们只需上传 1 张图片即可满足该要求。
归档时间: |
|
查看次数: |
14841 次 |
最近记录: |