我对架构的设计存在小混淆,但在开始之前,让我先向您展示架构,
CREATE TABLE Person
(
PersonID INT NOT NULL PRIMARY KEY,
FirstName VARCHAR(50),
LastName VARCHAR(50),
-- some columns here..
CONSTRAINT tb_idF INDEX (FirstName),
CONSTRAINT tb_idL INDEX (LastName)
-- or
-- CONSTRAINT tb_idL INDEX (FirstName, LastName)
-- other constraints ...
);
CREATE TABLE JobDescription
(
JobDescriptionID INT NOT NULL PRIMARY KEY,
JobDescriptionName VARCHAR(50) UNIQUE
-- some columns here..
-- constraints ...
);
并且混淆在这里,表格的映射表:Person
和JobDescription
。目前,我有这个设计,
CREATE TABLE Person_JobDescription
(
RECID INT AUTO_INCREMENT PRIMARY KEY, -- for some special reasons
-- I need to map to other table
PersonID INT,
JobDescriptionID INT,
StartYear INT, -- year JobDescription was Appointed
EndYear INT,
CONSTRAINT tb_fk1 FOREIGN KEY (PersonID)
REFERENCES Person(PersonID),
CONSTRAINT tb_fk2 FOREIGN KEY (JobDescriptionID)
REFERENCES JobDescription(JobDescriptionID),
CONSTRAINT tb_uq UNIQUE (PersonID, JobDescriptionID)
);
但是我有另外一个想法,映射表的结构将是这样的
CREATE TABLE Person_JobDescription
(
PersonID INT, -- map these two columns on the other table
JobDescriptionID INT, -- ^^
StartYear INT, -- year JobDescription was Appointed
EndYear INT,
CONSTRAINT tb_fk1 FOREIGN KEY (PersonID)
REFERENCES Person(PersonID),
CONSTRAINT tb_fk2 FOREIGN KEY (JobDescriptionID)
REFERENCES JobDescription(JobDescriptionID),
CONSTRAINT tb_pk PRIMARY KEY (PersonID, JobDescriptionID)
);
当我针对上表创建并测试查询时,它们都返回相同的结果,并且性能也与我在小型数据库(具有50k记录)上测试的性能相同。我想知道这两个查询在大型数据库上的表现如何。
问题
Person_JobDescription
)的两个模式中的哪一个?根据指示,我无法在UNIQUE
和FirstName
上创建LastName
约束。但我已经在两列上提供了一个索引。
Person
上使用哪种类型的索引?每列的索引或FirstName
和LastName
的复合索引?INDEX (Col1)
和INDEX (Col2)
而不是INDEX (Col1, Col2)
?感谢您花时间阅读此问题。
最诚挚的问候,
Derek Floss
答案 0 :(得分:7)
我更喜欢第二种方法。通过使用代理ID号在逻辑上不需要识别时,可以引入更多强制连接。这要求您“在整个数据库中追逐ID号”,这是相当于“在整个数据库中追逐指针”的SQL。追逐指针是IMS的特征,IMS是关系模型旨在取代的数据库架构之一。 (IMS使用分层架构。)今天重塑它是没有意义的。 (虽然很多人都这样做。)
例如,如果您有五个级别的代理ID号,并且您想要一个人的名字,则必须进行四次连接才能获得它。使用第二种方法,您只需要一次加入。如果您不想编写多列连接,请使用CREATE VIEW并执行一次。
性能易于测试。只需使用您喜欢的脚本语言生成几百万个随机行,然后将它们加载到测试服务器中。您不仅可以找到性能问题隐藏的位置,还可以在CREATE TABLE代码中找到所有错误。 (您的代码不会按原样运行。)如果您还不知道,请了解EXPLAIN。
对于索引,您可以在生成和加载的随机行上测试它。如果用户始终提供名字,则(first_name,last_name)上的多列索引将最有效。但是很多用户不会这样做,而是更喜欢用姓氏搜索。对于喜欢按姓氏搜索的用户,(first_name,last_name)上的多列索引无效。你可以测试一下。
仅仅因为这个原因,如果有两个单独的索引,一个用于名字,一个用于姓氏,则首字母和姓氏的索引通常通常更有效。
<小时/> 追逐ID号是什么意思?
这个问题的潜在设计模式是“每行必须有一个id号,所有外键都必须引用id号。”在SQL数据库中,它实际上是一种反模式。根据经验,任何可以让你在不考虑密钥的情况下设计表格的模式应该被认为是有罪的,直到被证明是无辜的 - 它应该被认为是一种反模式,直到证明不是。
create table A (
a_id integer primary key,
a_1 varchar(15) not null unique,
a_2 varchar(15) not null
);
create table B (
b_id integer primary key
a_id integer not null references A (a_id),
b_1 varchar(10) not null,
unique (a_id, b_1),
);
create table C (
c_id integer primary key,
b_id integer not null references B (b_id),
c_1 char(3) not null,
c_2 varchar(20) not null,
unique (b_id, c_1)
);
create table D (
d_id integer primary key,
c_id integer not null references C (c_id),
d_1 integer not null,
d_2 varchar(15),
unique (c_id, d_1)
);
如果您需要关于表“D”的报告,并且报告需要
你需要3个连接来实现它。 (试试吧。)你正在追逐身份证号码。 (就像在IMS中追逐指针一样。)以下结构不同。
create table A (
a_1 varchar(15) primary key,
a_2 varchar(15) not null
);
create table B (
a_1 varchar(15) not null references A (a_1),
b_1 varchar(10) not null,
primary key (a_1, b_1),
);
create table C (
a_1 varchar(15) not null,
b_1 varchar(10) not null,
c_1 char(3) not null,
c_2 varchar(20) not null,
primary key (a_1, b_1, c_1),
foreign key (a_1, b_1) references B (a_1, b_1)
);
create table D (
a_1 varchar(15) not null,
b_1 varchar(10) not null,
c_1 char(3) not null,
d_1 integer not null,
d_2 varchar(15),
primary key (a_1, b_1, c_1, d_1),
foreign key (a_1, b_1, c_1) references C (a_1, b_1, c_1)
);
使用此结构,同一报告需要一次加入。
select D.d_1, D.d_2, A.a_1, A.a_2
from D
inner join A on D.a_1 = A.a_1;
答案 1 :(得分:3)
第一种方法是我的偏好
如果您需要一个依赖于PersonJobDescription的表,比如AgentContact,您可以轻松链接到代理Rec_ID,没有它,您必须开始跳过篮球
另一个原因是,如果它成为每年持有人员/工作描述的要求会是什么? 在你知道自己在哪里之前,你将拥有一个仍然无法完成工作的四个复合钥匙。 规则复合主键应该是最后的手段,将使您的设计更具灵活性和弹性。