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 的意思是关系,它只是在列(或字段)上敲击一个唯一索引。

问题是,既然您声明 IDPRIMARY 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_lastname_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,则它不是关系表。

AUTOINCREMENTIDENTITY 仅适用于一件事:当且仅当您想在 SQL 数据库容器中创建一个 Excel 电子表格,其中包含名为 A, B,C, 的字段在顶部,并在左侧记录数字。在数据库术语中,这是 SELECT 的 result,数据的扁平视图,不是数据的来源,它是有组织(标准化)。

另一种可能(但不是首选)的解决方案可能是第一个表中有另一个主键,它是用户的用户名,当然不是自动递增语句。这是不可避免的吗?

在技术工作中,我们不关心偏好,因为这是主观的,并且一直在变化。我们关心技术上的正确性,因为这是客观的,不会改变。

是的,这是不可避免的。因为这只是时间问题;错误数量; “不能做”的数量;用户尖叫的次数,直到你面对事实,克服你的虚假声明,并意识到:

确保用户 是唯一的,即 user_names 是唯一的唯一方法是在其上声明 UNIQUE 约束

并去掉用户文件中的user_idid

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 个差异等返回不同的答案

从 2 个不同的数据库(MySQL 和 SQL)在 Listview 中插入值

在 2 个不同的列 sql 中获取 2 个不同日期的数据

如何从 2 个不同的 SQL Server 获取数据

我如何 SQL Join 2 个数据库,在一列中只有不同的结果