Mysql 数据库模式混乱(索引和约束)

Mysql 数据库模式混乱(索引和约束),mysql,sql,database,schema,Mysql,Sql,Database,Schema,我对模式的设计有点困惑,但在开始之前,让我先向您展示一下模式 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

我对模式的设计有点困惑,但在开始之前,让我先向您展示一下模式

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
jobsdescription
。现在我有这个设计,

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\u JobDescription
    )中的哪一种
按照说明,我不允许在
名字
姓氏
上创建
唯一
约束。但我提供了两列的索引

  • 我将在表
    Person
    上使用什么类型的索引?每列的索引或
    FirstName
    LastName
    的复合索引
  • 什么时候我会在
    索引(Col1,Col2)
    上使用单索引
    索引(Col1)
    索引(Col2)
感谢您花时间阅读这个问题

致以最良好的祝愿


德里克·弗洛斯(Derek Floss)

我更喜欢第一种方法

如果您需要一个依赖于PersonJobDescription的表,比如AgentContact,您可以轻松地链接到代理记录ID,如果没有它,您必须开始跳转

另一个原因是,如果要求每年保存一份人员/工作描述,该怎么办? 在你知道你在哪里之前,你会有一个四瓦库的复合钥匙,仍然不能完成工作。
复合主键规则应该是最后的手段,它将使您的设计更加灵活和有弹性。

我更喜欢第二种方法。通过在逻辑上不需要标识的情况下使用代理ID号,可以引入更多的强制联接。这要求您“在整个数据库中追踪ID号”,这是SQL等价于“在整个数据库中追踪指针”。追踪指针是IMS的特征,IMS是关系模型要取代的数据库体系结构之一。(IMS使用分层体系结构。)今天再创新它没有意义。(尽管很多人就是这么做的。)

例如,如果您有五个级别的代理ID号,并且您想要一个人的名字,那么您必须进行四次联接才能获得它。使用第二种方法,您只需要一个连接。如果不想编写多列联接,请使用“创建视图”并只执行一次

性能测试非常简单。只需使用您最喜欢的脚本语言生成几百万个随机ish行,并将它们加载到测试服务器中。您不仅可以找到性能问题隐藏的地方,还可以找到创建表代码中的所有错误。(您的代码无法正常工作。)如果您还不知道,请了解

至于索引,您可以在生成和加载的随机ish行上进行测试。如果用户总是提供名字,则对(first_name,last_name)的多列索引将最有效。但很多用户不会这么做,而是更喜欢按姓氏搜索。上的多列索引(first_name,last_name)对于喜欢按姓氏搜索的用户无效。你可以测试一下

仅出于这个原因,如果有两个单独的索引,一个用于名字,另一个用于姓氏,那么名字和姓氏的索引通常更有效


追逐身份证号码意味着什么?

这个问题的潜在设计模式是“每一行都必须有一个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”上的报告,则该报告需要

  • D.D_1和D.D_2列,以及
  • A.A_1和A.A_2列
你需要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;

每个
名字
都应该有一个
。你的意思是说每列的索引:
first\u name
last\u name
与这两列的复合索引相同?不,它们不相同。如果您有一个关于姓氏的索引和一个关于名字的索引,dbms可以使用它认为更快的索引。无论是按姓还是名搜索,用户都会得到快速响应。但是如果用户按名字搜索,{last_name,first_name}上的单个复合索引通常根本不会使用。从哪里获得额外的连接?一个额外的键/索引是的,但是您只加入一个值,而不是多个值。代理键的真正好处是长寿和弹性,没有代理键,业务类型不必做太多事情就把设计搞得一团糟,如果一开始值得做,它将持续并改变。@TonyHopkinson:编辑了我的答案。不能不同意这一点,只要a1到d1的规则没有变化。我倾向于将改变的弹性作为我的首选优化,现在,如果主要是针对报告数据库,那就没有任何争议了。我也想接受这个答案,但我也不会接受。非常感谢你!我已经投了你一票。