SQL:使用 2 个不同的 auto_increment 创建关系表
Posted
技术标签:
【中文标题】SQL:使用 2 个不同的 auto_increment 创建关系表【英文标题】:SQL: Creating a Relational table with 2 different auto_increment 【发布时间】:2015-06-20 18:12:05 【问题描述】:我有 2 个表,每个表都有自己的自动递增 IDs
,它们当然是主键。
当我想创建第三个表来建立这两个表之间的关系时,我总是出错。
第一个是你只能有 1 个自动递增的列,第二个是当我从这 2 个中删除 auto_increment 语句时发生的,因此 AQL 不允许我将它们设为外键,因为类型匹配失败.
有没有一种方法可以在不丢失自动增量功能的情况下创建关系表?
另一种可能(但不是首选)的解决方案可能是第一个表中有另一个主键,它是用户的用户名,当然不是使用 auto_increment 语句。这是不可避免的吗?
提前致谢。
【问题讨论】:
你的第三个表根本不应该有一个自动增量列,如果它是一个关系表,它应该只是从其他两个表中获取值。在这种情况下,自动增量毫无意义 我同意,但是当我尝试这样做时,我得到一个错误,因为与原始表的类型不匹配。 不应该是这种情况 - 请发布您的创建表语句 请查看答案并发表评论、投票、选择。 【参考方案1】:1 概念
您误解了一些基本概念,因此造成了困难。我们必须首先解决概念,而不是您认为的问题,因此,您的问题将消失。
自动递增的 ID,当然是主键。
不,他们不是。这是一个普遍的误解。并且肯定会出现问题。
ID
字段不能是英语、技术或关系意义上的主键。
当然,在 SQL 中,您可以将 any 字段声明为 PRIMARY KEY
,但这不会神奇地将其转换为英语、技术或关系意义上的主键。您可以将吉娃娃命名为“罗威纳犬”,但这并不能将其变成罗威纳犬,它仍然是吉娃娃。像任何语言一样,SQL 只是执行你给它的命令,它不理解 PRIMARY KEY
的意思是关系,它只是在列(或字段)上敲击一个唯一索引。
问题是,既然您声明 ID
是PRIMARY KEY
,您认为它是主键,您可能期望它具有主键的某些特性。除了 ID value 的唯一性之外,它没有任何好处。它没有主键或任何类型的关系键的品质。它不是英语、技术或关系意义上的 Key。将非键声明为键,只会让自己感到困惑,只有当用户抱怨表中有重复时,才会发现有什么可怕的错误。
2 关系模型
2.1 关系表必须具有行唯一性
ID
字段上的PRIMARY KEY
不提供行 唯一性。因此它不是一个包含行的关系表,如果不是,那么它就是一个包含记录的文件。它不具备关系数据库中的表所具有的完整性、能力(在此阶段您将只知道连接能力)或速度。
执行this code (MS SQL) 并向自己证明。请不要简单地阅读并理解它,然后继续阅读此答案的其余部分,在进一步阅读之前必须执行此代码。有治疗价值。
-- [1] Dumb, broken file
-- Ensures unique RECORDS, allows duplicate ROWS
CREATE TABLE dumb_file (
id INT IDENTITY PRIMARY KEY,
name_first CHAR(30),
name_last CHAR(30)
)
INSERT dumb_file VALUES
( 'Mickey', 'Mouse' ),
( 'Mickey', 'Mouse' ),
( 'Mickey', 'Mouse' )
SELECT *
FROM dumb_file
请注意您有重复的行。关系表必须具有唯一的行。进一步证明您没有关系表或任何关系表的性质。
请注意,在您的报告中,唯一独特的是 ID
字段,没有用户关心,没有用户看到,因为它不是数据,一些非常愚蠢的“老师”是一些额外的废话告诉你把每个文件。您有 record 唯一性,但没有 row 唯一性。
在数据方面(真实数据减去额外的添加),数据name_last
和name_first
可以在没有ID
字段的情况下存在。一个人有名字和姓氏,但额头上没有盖章。
您使用的第二个让您感到困惑的东西是AUTOINCREMENT.
如果您正在实施一个没有关系功能的记录归档系统,当然,这很有帮助,您不必在插入记录时编写增量代码。但是如果你正在实现一个关系数据库,它根本就没有任何用处,因为你永远不会使用它。 SQL 中有很多功能是大多数人从不使用的。
2.2 纠正措施
那么,您如何将充满重复行的dumb_file 升级、提升到关系表,以获得关系表的一些品质和好处?这需要三个步骤。
你需要了解钥匙
由于我们已经从 1970 年代的 ISAM 文件发展到 关系模型,因此您需要了解 关系键。也就是说,如果您希望获得关系数据库的优势(完整性、功能、速度)。
在 Codd 的关系模型中:
密钥由数据组成
和
表中的行必须是唯一的
您的“钥匙”不是由数据组成的。它是一些额外的、非数据的寄生虫,是由于你感染了你的“老师”的疾病而引起的。认清这一点,并让自己拥有上帝赋予你的全部心智能力(请注意,我不要求你以孤立、支离破碎或抽象的方式思考,数据库中的所有元素必须相互整合)。
由数据构成一个真正的密钥,并且仅由数据构成。在这种情况下,只有一个可能的 Key:(name_last, name_first).
Try this code,声明对数据的唯一约束:
-- [2] dumb_file fixed, elevated to table, prevents duplicate rows
-- still dumb
CREATE TABLE dumb_table (
id INT IDENTITY PRIMARY KEY,
name_first CHAR(30),
name_last CHAR(30),
CONSTRAINT UK
UNIQUE ( name_last, name_first )
)
INSERT dumb_table VALUES
( 'Mickey', 'Mouse' ),
( 'Minnie', 'Mouse' )
SELECT *
FROM dumb_table
INSERT dumb_table VALUES
( 'Mickey', 'Mouse' )
现在我们有了行唯一性。这是发生在大多数人身上的顺序:他们创建一个允许欺骗的文件;他们不知道为什么会出现在下拉列表中的骗子;用户尖叫;他们调整文件并添加索引以防止欺骗;他们去下一个错误修复。 (他们这样做可能正确与否,那是另一回事。)
第二层。对于那些思考超出固定范围的人。既然我们现在有行唯一性,那么 ID
字段的目的到底是什么,我们为什么还要拥有它???哦,因为吉娃娃叫Rotty,我们不敢碰它。
它是PRIMARY KEY
的声明是错误的,但它仍然存在,导致混乱和错误的期望。唯一真正的密钥是(name_last, name_fist),
,此时它是备用密钥。
因此ID
字段是完全多余的;支持它的索引也是如此;愚蠢的AUTOINCREMENT
也是如此;错误声明它是PRIMARY KEY
也是如此;你对它的任何期望都是错误的。
因此删除多余的ID
字段。 Try this code:
-- [3] Relational Table
-- Now that we have prevented duplicate data, the id field
-- AND its additional index serves no purpose, it is superfluous,
-- like an udder on a bull. If we remove the field AND the
-- supporting index, we obtain a Relational table.
CREATE TABLE relational_table (
name_first CHAR(30),
name_last CHAR(30),
CONSTRAINT PK
PRIMARY KEY ( name_last, name_first )
)
INSERT relational_table VALUES
( 'Mickey', 'Mouse' ),
( 'Minnie', 'Mouse' )
SELECT *
FROM relational_table
INSERT relational_table VALUES
( 'Mickey', 'Mouse' )
工作正常,按预期工作,没有多余的字段和索引。
请记住这一点,并且每次都做对。
2.3 假教师
在这些末世,正如我们所建议的,我们将拥有许多这样的时代。请注意,传播ID
列的“老师”,凭借本文中的详细证据,根本不了解关系模型或关系数据库。尤其是那些写书的人。
正如所证明的,他们被困在 1970 年之前的 ISAM 技术中。这就是他们所了解的一切,这就是他们所能教的一切。他们使用 SQL 数据库容器,以便于访问、恢复、备份等,但内容是纯粹的记录归档系统,没有关系完整性、功能或速度。 AFAIC,这是严重的欺诈行为。
当然,除了ID
字段之外,还有几个关键的Relational-or-not 概念,放在一起,使我形成如此严肃的结论。这些其他项目超出了本文的范围。
目前有一对特定的白痴正在对第一范式发起攻击。他们属于庇护所。
3 解决方案
现在回答你剩下的问题。
3.1 答案
有没有一种方法可以在不丢失自动增量功能的情况下创建关系表?
这是一个自相矛盾的句子。我相信你会从我的解释中明白,AUTOINCREMENT
“功能”的关系表不需要;如果文件有AUTOINCREMENT
,则它不是关系表。
AUTOINCREMENT
或 IDENTITY
仅适用于一件事:当且仅当您想在 SQL 数据库容器中创建一个 Excel 电子表格,其中包含名为 A,
B,
和 C,
的字段在顶部,并在左侧记录数字。在数据库术语中,这是 SELECT 的 result,数据的扁平视图,不是数据的来源,它是有组织(标准化)。
另一种可能(但不是首选)的解决方案可能是第一个表中有另一个主键,它是用户的用户名,当然不是自动递增语句。这是不可避免的吗?
在技术工作中,我们不关心偏好,因为这是主观的,并且一直在变化。我们关心技术上的正确性,因为这是客观的,不会改变。
是的,这是不可避免的。因为这只是时间问题;错误数量; “不能做”的数量;用户尖叫的次数,直到你面对事实,克服你的虚假声明,并意识到:
确保用户 行 是唯一的,即 user_names 是唯一的唯一方法是在其上声明 UNIQUE
约束
并去掉用户文件中的user_id
或id
将user_name
提升为PRIMARY KEY
是的,因为您与第三张表的整个问题,并非巧合,然后被消除了。
第三个表是关联表。唯一需要的键(主键)是两个父主键的组合。这确保了 行 的唯一性,这些行由它们的键标识,而不是由它们的 IDs.
我警告你,因为那些教你实现ID
字段错误的“老师”,教你在关联表中实现ID
字段的错误,就像普通表一样,它是多余的,没有任何用处,引入重复,并引起混乱。而且它是双重多余的,因为提供的两把钥匙已经在那里,盯着我们的脸。
由于他们不理解 RM 或关系术语,他们将关联表称为“链接”或“映射”表。如果它们有 ID
字段,它们实际上就是文件。
3.2 查找表
ID
字段对于查找表或参考表来说是特别愚蠢的事情。它们中的大多数都有可识别的代码,无需枚举其中的代码列表,因为代码是(应该)唯一的。
ENUM
同样愚蠢,但出于不同的原因:它将您锁定在一种反 SQL 方法中,即不符合标准的“SQL”中的“功能”。
此外,将子表中的代码作为 FK 是一件好事:代码更有意义,并且通常可以节省不必要的连接:
SELECT ...
FROM child_table -- not the lookup table
WHERE gender_code = "M" -- FK in the child, PK in the lookup
代替:
SELECT ...
FROM child_table
WHERE gender_id = 6 -- meaningless to the maintainer
或更糟:
SELECT ...
FROM child_table C -- that you are trying to determine
JOIN lookup_table L
ON C.gender_id = L.gender_id
WHERE L.gender_code = "M" -- meaningful, known
请注意,这是无法避免的:您需要查找代码的唯一性和描述的唯一性。这是防止两列中每个重复的唯一方法:
CREATE TABLE gender (
gender_code CHAR(2) NOT NULL,
name CHAR(30) NOT NULL
CONSTRAINT PK
PRIMARY KEY ( gender_code )
CONSTRAINT AK
UNIQUE ( name )
)
3.3 完整示例
从你问题中的细节来看,我怀疑你有SQL语法和FK定义问题,所以我会给出你需要的整个解决方案作为例子(因为你没有给出文件定义):
CREATE TABLE user ( -- Typical Identifying Table
user_name CHAR(16) NOT NULL, -- Short PK
name_first CHAR(30) NOT NULL, -- Alt Key.1
name_last CHAR(30) NOT NULL, -- Alt Key.2
birth_date DATE NOT NULL -- Alt Key.3
CONSTRAINT PK -- unique user_name
PRIMARY KEY ( user_name )
CONSTRAINT AK -- unique person identification
PRIMARY KEY ( name_last, name_first, birth_date )
)
CREATE TABLE sport ( -- Typical Lookup Table
sport_code CHAR(4) NOT NULL, -- PK Short code
name CHAR(30) NOT NULL -- AK
CONSTRAINT PK
PRIMARY KEY ( sport_code )
CONSTRAINT AK
PRIMARY KEY ( name )
)
CREATE TABLE user_sport ( -- Typical Associative Table
user_name CHAR(16) NOT NULL, -- PK.1, FK
sport_code CHAR(4) NOT NULL, -- PK.2, FK
start_date DATE NOT NULL
CONSTRAINT PK
PRIMARY KEY ( user_name, sport_code )
CONSTRAINT user_plays_sport_fk
FOREIGN KEY ( user_name )
REFERENCES user ( user_name )
CONSTRAINT sport_occupies_user_fk
FOREIGN KEY ( sport_code )
REFERENCES sport ( sport_code )
)
那里,PRIMARY KEY
声明是诚实的,它是一个主键;没有ID;
没有AUTOINCREMENT;
没有额外的索引;没有重复的行;没有错误的期望;没有相应的问题。
3.4 关系数据模型
这是与定义一起使用的数据模型。
作为PDF
如果您不习惯符号,请注意,每一个小勾号、缺口和标记,实线与虚线,方角与圆角,都意味着非常具体的东西。参考IDEF1X Notation。
一张图片胜过千言万语;在这种情况下,标准投诉图片的价值远不止于此;一张糟糕的纸不值得画它。
请仔细检查动词短语,它们包含一组谓词。谓词的其余部分可以直接从模型中确定。如果不清楚,请询问。
【讨论】:
@Hamster_NL。 (d) 将其记录在真实的 RDb 中意味着 BirthPlace 被规范化为至少 CountryCode + Town,有时还需要 StateCode。我总是使用后者,我从来没有升级过。这意味着需要首先填充这些表。 (e) 这样的 PK 太宽,对于迁移到子节点来说是一个不好的选择,但它不能被放弃,因为它提供了行唯一性。因此,使 PK 成为 AK,并为 PK 创建一个代理项。 PK 太宽,是代理的唯一且唯一有效的理由。如果您想了解更多细节,请提出新问题,我会回答。 @Hamster_NL。我在 Answer 中添加了一个小型数据模型,并为 Person 提供了完整的 quid。 我知道这是一篇旧帖子,但您是否认真建议使用人名作为主键?!?一:它不是唯一的,很多人有相同的名字(有些人甚至有相同的出生日期)。第二:它不是一成不变的。人们可以更改他们的名字,所以在软件中也应该可以。 2) 性能注意事项。这绝不应该限制逻辑 [DM] 设计。所有正版 SQL 平台对复合 Key 完全没有问题;宽键;等永远不要在密钥中使用varAnything
,这在任何平台上都是愚蠢的。始终在键中使用固定宽度。 3) DATETIME
DataType 在 SQL 平台上具有微秒级的分辨率。在伪装者上,添加Sequence
列。 4) 唯一信息根本不存在 关系模型* 需要唯一的行。所以制造一个列。 5) 您还没有意识到 ID
列,以任何形式,不能唯一化一行。
@Maggyero 1) (ArtistName, Birthdate)
或 (CountryCode, ArtistName)
2) 因为它是教科书示例,并非旨在渲染现实。当您从教科书转向现实时,是的,您将添加许多表格。这完全没有说明教科书的例子。以上是关于SQL:使用 2 个不同的 auto_increment 创建关系表的主要内容,如果未能解决你的问题,请参考以下文章
SQL:使用 2 个不同的 auto_increment 创建关系表
比较 SQL 表行,如果完全匹配、1 个差异、2 个差异等返回不同的答案