【问题标题】:Composite primary key comprising two foreign keys referencing same table: SQL Server vs. MySQL复合主键包含两个引用同一个表的外键:SQL Server vs. MySQL
【发布时间】:2010-09-10 18:30:26
【问题描述】:

我已经阅读了许多关于常见的一对多/用户对朋友场景的数据库表设计的帖子。一篇文章包括以下内容:

用户

* user_id (primary key)
* username

朋友

* user_id (primary key, foreign key to USERS(user_id))
* friend_id (primary key, foreign key to USERS(user_id))

> 这将停止重复(IE:1、2) 从发生,但不会停止 反转,因为 (2, 1) 是有效的。 你需要一个触发器来强制执行 只有一个实例 关系...

粗体部分促使我提出我的问题:SQL Server 和 MySQL 如何处理这些类型的复合键有区别吗?两者都需要海报提到的这个触发器,以确保唯一性吗?

我问,因为到目前为止,我一直在 SQL Server 中使用类似的表结构,但没有任何此类触发器。我只是幸运地没有遇到这条潜伏在草丛中的数据复制蛇吗?

【问题讨论】:

  • 可能是我见过的最好的用户名...
  • 谢谢。我非常认真地对待自己......

标签: mysql sql-server database-design


【解决方案1】:

是的,所有 DBMS 都会一视同仁。原因是 DBMS 假定该列是有意义的。即,元组不是由无意义的数字组成的。每个属性都有意义。假定user_idfriend_id 具有不同的含义。因此,设计者有责任建立一个声称 1,2 等于 2,1 的规则。

【讨论】:

  • 哇,我无法告诉你我见过有多少示例/教程根本没有提及这些潜在的数据重复。
  • @PolishedTurd - 数据库无法知道它是重复的。是设计师声明它们是等价的。想象这两列是manager_idsubordinate_id。订单在那里有所作为。一个项目可能有 1,2,另一个项目可能有 2,1。
  • 现在更有意义了。伙计,那里有很多误导性的学习材料。
【解决方案2】:

您可以只使用friend_id > user_id 的检查约束来防止“逆转”。这将强制无法输入诸如(2, 1) 之类的对,这样的关系必须输入为(1, 2)

【讨论】:

  • 那么 1,2 和 2,1 被认为是两个独特的记录吗?原谅我的无知。
  • 它们在 SQL Server 和 MySQL 中的处理方式是否相同?
  • 解决了这个问题,但需要在客户端代码中进行一些小的箍跳,以始终将较低值的 id 放在特定列中。
  • Thomas:但是您想在客户端的每个实例中进行检查吗?我说的是应用程序代码实例——Java、C/C++、.NET、Perl、Ruby、Python……当检查约束(MySQL 不支持)或触发器可以减轻这种依赖并集中确保数据时。
  • @OMG Ponies - 检查约束只是强制执行规则。除非数据库端 CRUD 代码(即存储过程),所有客户端代码必须具有将值发布到正确顺序的逻辑,这对于应用程序开发人员来说显然很尴尬。
【解决方案3】:

如果你的友谊关系是对称的,你需要在表定义中添加一个CHECK(user_id < friend_id),并像这样插入数据:

INSERT
INTO    friends
VALUES  (
        (CASE user_id < friend_id THEN user_id ELSE friend_id END),
        (CASE user_id > friend_id THEN user_id ELSE friend_id END)
        )

SQL Server 中,您可以在一对计算列上构建UNIQUE 索引:

CREATE TABLE friends (orestes INT, pylades INT, me AS CASE WHEN orestes < pylades THEN orestes ELSE pylades END, friend AS CASE WHEN orestes > pylades THEN orestes ELSE pylades END)

CREATE UNIQUE INDEX ux_friends_me_friend ON friends (me, friend)

INSERT
INTO    friends
VALUES  (1, 2)

INSERT
INTO    friends
VALUES  (2, 1)
-- Fails

要获取给定用户的所有朋友,您需要运行以下查询:

SELECT  friend_id
FROM    friends
WHERE   user_id = @myuser
UNION ALL
SELECT  user_id
FROM    friends
WHERE   friend_id = @myuser

但是,在MySQL 中,始终保留每对的两个副本可能更有效。

您可能会发现这些文章很有趣:

【讨论】:

  • 是的,但是 MySQL 有 CHECK 约束,它不会在任何引擎上强制执行。
  • @OMGPonies:当然。但是,如果您始终正确运行查询,则不需要约束。
  • 那篇文章很棒。谢谢。
【解决方案4】:

如果关系是对称的,那么另一种选择是在数据库中将关系“定义”为不对称的,但每次添加任何一个元组时总是添加两个元组。

您基本上是在说“友谊的性质在 DB 中是不对称的,A 可以成为 B 的朋友,而 B 不是 A 的朋友,但应用程序将始终添加(或删除)两个记录 (a,B) 和 (B, A) 任何时候我添加(删除)。这也简化了查询逻辑,因为您不必再​​查看两列。每次修改数据时额外插入/删除一次,但查询时读取次数减少...

【讨论】:

    猜你喜欢
    • 2023-03-17
    • 2017-01-16
    • 1970-01-01
    • 2012-07-02
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 2019-12-23
    • 1970-01-01
    相关资源
    最近更新 更多