1

我刚开始学习 SQL(使用 SQLite),我试图弄清楚什么时候应该使用外键。向我解释的方式是,只要出现重复数据,就应该使用外键,只需保存 ID 以节省空间。我正在创建的数据库中有几千条记录,列出了类别和县(每列中可能有几十个唯一值)。所以我可以用县名和主键 id 为县制作一个单独的表,并对类别做同样的事情。我毫不怀疑它会使数据库缩小约 5%。但这是唯一的好处吗?似乎它使其他一切变得更加复杂。为原本不需要的县和类别添加 ID。在 phpLiteAdmin 中查看表格时,它只显示一个数字而不是类别/县名,使其更难以可视化。在这种情况下使用外键并制作单独的表有什么好处?还是我不应该这样做,而是将所有数据(重复和所有数据)都放在一张表中?另外 - 让县/类别表只有一列没有数字主键是否有意义,因为无论如何它们都是唯一的?这至少会在 phpLiteAdmin 中显示全名。提前致谢!在这种情况下使用外键并制作单独的表有什么好处?还是我不应该这样做,而是将所有数据(重复和所有数据)都放在一张表中?另外 - 让县/类别表只有一列没有数字主键是否有意义,因为无论如何它们都是唯一的?这至少会在 phpLiteAdmin 中显示全名。提前致谢!在这种情况下使用外键并制作单独的表有什么好处?还是我不应该这样做,而是将所有数据(重复和所有数据)都放在一张表中?另外 - 让县/类别表只有一列没有数字主键是否有意义,因为无论如何它们都是唯一的?这至少会在 phpLiteAdmin 中显示全名。提前致谢!

4

5 回答 5

3

如果您使用外键。它也称为参照完整性。

假设您有两个表,第一个表是 account_user,第二个表是 account_user_detail。因此 account_user 表将具有 account_id 的 account_number 的主键。account_user_detail 表将包含帐户持有人地址详细信息。因此,如果您将两个表关联起来,那么 account_number 或 account_id 将是相同的。所以使用第二个表中的主键值我们定义外键。外键标识第二个表中 account_number 的值是第一个表中具有相同帐号的 Xyz 先生的引用。

因此,外键用于将两个表与两个表共有的列连接起来,并共享相同的唯一值。

于 2013-09-21T09:10:21.367 回答
1

你可以检查这个

SQL 外键约束用于强制表之间的“存在”关系。

编辑:-

存在外键约束是为了保证引用的行存在。

维基也说: -

数据库设计的一个重要部分是确保真实世界实体之间的关系通过引用反映在数据库中,使用外键从一个表引用到另一个表。 [9] 数据库设计的另一个重要部分是数据库规范化,其中表被分解,外键使它们可以被重建。

还要检查这个线程。

为什么在理论上比在实践中更多地使用外键?

于 2013-09-21T08:59:41.187 回答
0

但这是唯一的好处吗?

不。

外键在逻辑上类似于大多数编程语言中的指针或引用。想象一下,试图通过复制数据来创建一些数据结构,而不能引用任何东西。没有外键的数据库也会有同样的问题。

如果没有引用事物的能力,您必须确保所有副本都是最新的。如果存在导致一个副本被更新但另一个副本没有更新的错误,那将有效地破坏数据——您将不再知道哪个副本是正确的。

避免冗余主要不是关于空间,而是关于数据完整性。数据库规范化(没有外键就无法完成)的全部目的是避免冗余,从而保护数据完整性。


在您的特定情况下...

  • 一个类别(或国家)是否应该能够在不连接到主表中的任何行的情况下存在?
  • 一个类别是否应该存在任何数据,独立于该类别连接到主表中的哪些行?
  • 是否有任何操作(如重命名)应该独立完成?

如果任一答案为“是”,则应将类别放入单独的查找表中。此查找表是否应该使用自然(名称)或代理(ID)键是一个不同的问题。这里列出了一些优点和缺点。

于 2013-09-21T12:24:36.767 回答
0

外键约束用于限制允许存在于一列或一组列中的值。以婚姻为例:

CREATE TABLE person
        (person_id INTEGER NOT NULL PRIMARY KEY
        , name varchar NOT NULL
        );

CREATE TABLE marriage
        ( person1 INTEGER NOT NULL PRIMARY KEY
        , person2 INTEGER NOT NULL UNIQUE
        , comment varchar
        , CONSTRAINT marriage_1 FOREIGN KEY (person1) REFERENCES person(person_id)
        , CONSTRAINT marriage_2 FOREIGN KEY (person2) REFERENCES person(person_id)
        , CONSTRAINT order_in_court CHECK (person1 < person2)
        );

-- add some data ...
INSERT INTO person(person_id,name) values (1,'Bob'),(2,'Alice'),(3,'Charles');

INSERT INTO marriage(person1,person2, comment) VALUES(1,2, 'Crypto marriage!') ; -- Ok
INSERT INTO marriage(person1,person2, comment) VALUES(2,1, 'Not twice!' ) ; -- Should fail
INSERT INTO marriage(person1,person2, comment) VALUES(3,3, 'No you dont...' ) ; -- Should fail
INSERT INTO marriage(person1,person2, comment) VALUES(2,3, 'OMG she did it again.' ) ; -- Should fail (does not)
INSERT INTO marriage(person1,person2, comment) VALUES(3,4, 'Non existant persons are not allowed to marry !' ) ; -- Should fail

SELECT p1.name, p2.name, m.comment
FROM marriage m
JOIN person p1 ON m.person1 = p1.person_id
JOIN person p2 ON m.person2 = p2.person_id
        ;

上面的 DDL 尝试对婚姻进行建模(并且部分失败)要建模的约束是:

  • 只有现有的人可以结婚
  • 婚姻只能存在于两个不同的人之间
  • 一个人只能结一次婚

输出:

INSERT 0 3
INSERT 0 1
ERROR:  new row for relation "marriage" violates check constraint "order_in_court"
ERROR:  new row for relation "marriage" violates check constraint "order_in_court"
INSERT 0 1
ERROR:  insert or update on table "marriage" violates foreign key constraint "marriage_2"
DETAIL:  Key (person2)=(4) is not present in table "person".
 name  |  name   |        comment        
-------+---------+-----------------------
 Bob   | Alice   | Crypto marriage!
 Alice | Charles | OMG she did it again.
(2 rows)
于 2013-09-21T13:08:51.560 回答
0

如果您的国家名称是“美利坚合众国”,则为 24 字节。如果使用外键,则只需要 2-4 个字节。那是一个巨大的差异。

当您搜索国家名称时,它会非常快,因为您只需匹配一个数字而不是整个字符串。

此外,如果您在 country_id 字段上使用索引,它会变得更小。

我能理解你关于增加复杂性的观点。在您的情况下,您可以不使用外键,但您不应该这样做。您最终将需要它们,以便更好地准备和体验该主题。

于 2013-09-21T09:22:31.893 回答