避免复合键的最佳实践?

mot*_*our 5 sql-server primary-key

假设我的学生每年都填写一份调查问卷,其中包含一些问题(称为字段),这些问题的答案是实值(称为值)。每个学生可以回答调查,最多一年一次。

一个简单的调查示例:

  • 2012年:讲座质量(0-100)
  • 2013年:讲座质量(0-100)和难度(0-100)

我面临一个困境:使用复合键或检查约束。哪种解决方案是最佳实践?

没有复合键:
我创建了以下表结构,但我需要一个约束来确保它field.yearsurvey.year

CREATE TABLE survey (
   surveyID int primary key,
   student_name nvarchar(20),
   year int,
)

CREATE TABLE fields (
   fieldID int primary key,
   field_name nvarchar(20),
   year int,
)

CREATE TABLE data(
   surveyID int,
   fieldID int,
   value float,
   primary key (surveyID, fieldID), 
   foreign key(studentID), 
   foreign key(fieldID)
)
Run Code Online (Sandbox Code Playgroud)

使用复合键:
使用以下表结构,我有一个缺点,即为(surveyID, year)我进行的每个连接使用元组。

CREATE TABLE survey (
   surveyID int primary key,
   student_name nvarchar(20),
   year int,
   primary key (surveyID, year)
)

CREATE TABLE fields (
   fieldID int primary key,
   field_name nvarchar(20),
   year int,
   primary key(fieldID)
)

CREATE TABLE data(
   surveyID int,
   fieldID int,
   year int,
   value float,
   primary key (surveyID, year, fieldID), 
   foreign key(studentID, year), 
   foreign key(fieldID, year)
)
Run Code Online (Sandbox Code Playgroud)

Ken*_*her 12

从正确的主键开始(单列 vs 多列或人工 vs 自然)是使用对任务正确的主键。

有些人会告诉您始终使用ArtificialSurrogate键。这是一个密钥,它是一个生成的密钥,与它所代表的信息无关。例如标识列或 GUID。其他人会告诉你 anartificial key是错误的方法,你想使用Natural Key. 这是从一列或多列数据本身构建的键。仅供参考,具有多列的键有时称为 a composite key。例如姓氏、名字。或者在您的情况下,您似乎正在使用两者的组合。您有一个整数 ID(大概是人为的)+ 一年(大概是自然的)。

我不打算在这里讨论争论。你可以自己谷歌一下。在您的特定情况下,我会不理会您的主键。在我看来,将 SurveyId 和 FieldId 列作为主键是有意义的。这些是唯一值(我假设),在这种特殊情况下,这些表基本上是查找表。但是,我会将 Year 列从这些表中移到data表中。如果您不想这样做,请利用外键可以连接到“唯一键”以及primary key. Aunique key是强制唯一性的索引,但必须是主键。由于 a 中的附加奖励列unique key可以为空。在这种情况下,您的结构将如下所示:

CREATE TABLE survey (
   surveyID int primary key,
   student_name nvarchar(20),
   year smallint
)
GO

CREATE UNIQUE INDEX ix_survey ON survey(surveyID, year)
GO

CREATE TABLE fields (
   fieldID int primary key,
   field_name nvarchar(20),
   year smallint
)
GO

CREATE UNIQUE INDEX ix_fields ON fields(fieldID, year)
GO

CREATE TABLE data(
   surveyID int,
   fieldID int,
   year smallint,
   value float,
   primary key (surveyID, year, fieldID), 
   foreign key(surveyID, year) REFERENCES survey(surveyID, year), 
   foreign key(fieldID, year) REFERENCES fields(fieldID, year)
)
GO
Run Code Online (Sandbox Code Playgroud)

我确实冒昧地将年份列更改为 smallint,它大到足以容纳 4 位数的年份,并且只需要 2 个字节而不是 4 个字节。

使用unique key可以强制您希望年份列相同,同时不向主键添加不必要的列(ID 已经是唯一的)。


Van*_*Van 1

假设你正在使用 MySQL....

恕我直言,最佳实践是避免使用复合主键 - 特别是由行数据组成。原因是 a) 主键(至少在 MySQL 中)是为外键和索引约束存储的。这有助于最小化索引大小、排序和更新;b) 如果您必须更改表中的数据或结构,它有助于避免混乱且昂贵的 FK 约束更新。因此,每个表通常都有一个与表数据无关的主键。基本上你有一个唯一的记录 id

另外,作为旁注,我建议主键为无符号整数(因为您实际上永远不会有 -101 主键)以最大化您的存储容量。在 MySQL 中我会推荐 BIGINT。

因此,您的NO COMPOSITE键示例更好(此处针对 MySQL 进行了优化):

CREATE TABLE survey (
 surveyID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
 student_name varchar(20),
 year int,
 primary key (surveyID)
)

CREATE TABLE fields (
 fieldID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
 field_name varchar(20),
 year int,
 primary key (fieldID)
)

CREATE TABLE data(
  dataID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  surveyID BIGINT UNSIGNED NOT NULL,
  fieldID BIGINT UNSIGNED NOT NULL,
  value float,
  primary key (dataID),
  CONSTRAINT data_surveyID FOREIGN KEY (surveyID) REFERENCES survey (surveyID) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT data_fieldID FOREIGN KEY (fieldID) REFERENCES field (fieldID) ON DELETE CASCADE ON UPDATE CASCADE,
)
Run Code Online (Sandbox Code Playgroud)

  • 该问题标有“[sql-server]” (4认同)