8

我对架构的设计有点困惑,但在我开始之前,让我先向您展示架构,

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 ...
);

混乱就在这里,表的映射表:PersonJobDescription。目前,我有这个设计,

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在大型数据库中,您更喜欢映射表 ( ) 的两种模式中的哪一种?

按照指示,我不允许在and上创建UNIQUE约束。但我已经在这两列上提供了一个索引。FirstNameLastName

  • 我将在 table 上使用什么类型的索引PersonFirstName每列的索引或和的复合索引LastName
  • 我什么时候会使用单索引INDEX (Col1)INDEX (Col2)以上INDEX (Col1, Col2)

感谢您花时间阅读这个问题。

最好的祝福,

德里克·弗洛斯

4

2 回答 2

7

我更喜欢第二种方法。通过在逻辑上不需要标识时使用代理 ID 号,您可以引入更多强制连接。这需要你“在整个数据库中追逐 ID 号”,这相当于“在整个数据库中追逐指针”的 SQL。追逐指针是 IMS 的特征,IMS 是关系模型打算取代的数据库架构之一。(IMS 使用分层架构。)今天重新发明它是没有意义的。(虽然很多人就是这么做的。)

例如,如果您有五个级别的代理 ID 号,并且您想要一个人的姓名,则必须进行四次连接才能得到它。使用第二种方法,您只需要一个连接。如果您不想编写多列连接,请使用 CREATE VIEW 并只执行一次。

性能很容易测试。只需使用您喜欢的脚本语言生成几百万随机行,然后将它们加载到测试服务器中。您不仅会发现性能问题隐藏的位置,还会发现 CREATE TABLE 代码中的所有错误。(您的代码不会按原样工作。)如果您还不了解EXPLAIN ,请了解它。

至于索引,您可以在生成和加载的随机行上进行测试。如果用户始终提供名字,则 (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;
于 2012-12-15T17:00:34.707 回答
3

第一种方法是我的偏好

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

另一个原因是,如果要求每年持有一个 Person/JobDescription 会怎样?在您知道自己在哪里之前,您将拥有一个仍然无法完成工作的四个 vakue 复合键。规则复合主键应该是最后的手段,这将使您的设计更加灵活和有弹性。

于 2012-12-15T16:21:21.220 回答