使用 MySQL 外键引用多列

Wax*_*age 4 mysql foreign-keys composite

我只是偶然发现了 MySQL 外键引用多个列的可能性。我想知道如下所示的多列外键的主要目的是什么

ALTER TABLE `device` 
ADD CONSTRAINT `fk_device_user`
  FOREIGN KEY (`user_created_id` , `user_updated_id` , `user_deleted_id`)
  REFERENCES `user` (`id` , `id` , `id`)
  ON DELETE NO ACTION
  ON UPDATE NO ACTION;
Run Code Online (Sandbox Code Playgroud)

我的问题是

  1. 和创建三个独立的外键一样吗?
  2. 使用一种或另一种有什么优点/缺点吗?
  3. 这个的确切用例是什么?(主要问题)

Dre*_*rew 6

  1. 和创建三个独立的外键一样吗?

不,请考虑以下问题。

首先,将其视为 (id,id,id) 是没有用的,而实际上是 (id1,id2,id3)。因为 (id,id,id) 的元组在id. 因此,您将看到下面描述的模式。

create schema FKtest001;
use FKtest001;

create table user
(   id int auto_increment primary key,
    fullname varchar(100) not null,
    id1 int not null,
    id2 int not null,
    id3 int not null,
    index `idkUserTuple` (id1,id2,id3)
);

create table device
(   id int auto_increment primary key,
    something varchar(100) not null,
    user_created_id int not null,
    user_updated_id int not null,
    user_deleted_id int not null,
    foreign key `fk_device_user` (`user_created_id` , `user_updated_id` , `user_deleted_id`)
       REFERENCES `user` (`id1` , `id2` , `id3`)

);
show create table device;
CREATE TABLE `device` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `something` varchar(100) NOT NULL,
   `user_created_id` int(11) NOT NULL,
   `user_updated_id` int(11) NOT NULL,
   `user_deleted_id` int(11) NOT NULL,
   PRIMARY KEY (`id`),
   KEY `fk_device_user` (`user_created_id`,`user_updated_id`,`user_deleted_id`),
   CONSTRAINT `device_ibfk_1` FOREIGN KEY (`user_created_id`, `user_updated_id`, `user_deleted_id`) REFERENCES `user` (`id1`, `id2`, `id3`)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
show indexes from device; -- shows 2 indexes (a PK, and composite BTREE)
-- FOCUS heavily on the `Seq_in_index` column for the above

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

drop table device;
drop table user;

create table user
(   id int auto_increment primary key,
    fullname varchar(100) not null,
    id1 int not null,
    id2 int not null,
    id3 int not null,
    index `idkUser1` (id1),
    index `idkUser2` (id2),
    index `idkUser3` (id3)
);

create table device
(   id int auto_increment primary key,
    something varchar(100) not null,
    user_created_id int not null,
    user_updated_id int not null,
    user_deleted_id int not null,
    foreign key `fk_device_user1` (`user_created_id`)
       REFERENCES `user` (`id1`),
    foreign key `fk_device_user2` (`user_updated_id`)
       REFERENCES `user` (`id2`),
    foreign key `fk_device_user3` (`user_deleted_id`)
       REFERENCES `user` (`id3`)
);
show create table device;
CREATE TABLE `device` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `something` varchar(100) NOT NULL,
   `user_created_id` int(11) NOT NULL,
   `user_updated_id` int(11) NOT NULL,
   `user_deleted_id` int(11) NOT NULL,
   PRIMARY KEY (`id`),
   KEY `fk_device_user1` (`user_created_id`),
   KEY `fk_device_user2` (`user_updated_id`),
   KEY `fk_device_user3` (`user_deleted_id`),
   CONSTRAINT `device_ibfk_1` FOREIGN KEY (`user_created_id`) REFERENCES `user` (`id1`),
   CONSTRAINT `device_ibfk_2` FOREIGN KEY (`user_updated_id`) REFERENCES `user` (`id2`),
   CONSTRAINT `device_ibfk_3` FOREIGN KEY (`user_deleted_id`) REFERENCES `user` (`id3`)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
 show indexes from device; -- shows 4 indexes (a PK, and 3 indiv FK indexes)
-- FOCUS heavily on the `Seq_in_index` column for the above
Run Code Online (Sandbox Code Playgroud)

那里有2个部分。在show indexes from device将显示的差异,在顶部,2个指标保持不变。在底部,维护了 4 个索引。如果由于某种原因顶部的索引元组对系统有用,那么该元组方法肯定是要走的路。

原因如下。元组作为一个组存在。将其视为具有作为组的含义的集合的实例。与仅存在单个部分相比,这是有区别的。并不是用户存在,而是有一个用户行将该元组作为存在。

  1. 使用一种或另一种有什么优点/缺点吗?

优点在上面最后一段中进行了描述:作为user表中的实际分组作为元组存在。

它们是苹果和橙子,用于不同的目的。

  1. 这个的确切用例是什么?(主要问题)

用例是需要元组作为一个组存在的东西,而不是单个项目的存在。它用于所谓的合成。特别是合成 FK。请参阅我的这个答案这里作为一个案例。

简而言之,当您想要在其他实体的复合级别(分组)上强制执行特别难以想到的解决方案时,需要参考完整性 (RI)。许多人认为它无法完成,因此他们首先考虑TRIGGER执行或前端执行。幸运的是,这些用例可以通过 FK Composites 实现,从而将 RI 留在它应该在的 db 级别(而不是在前端)。

附录

向 OP 请求比上面的链接更好的现实生活示例。

考虑以下模式:

CREATE SCHEMA testRealLifeTuple;
USE testRealLifeTuple;

CREATE TABLE contacts
(   id INT AUTO_INCREMENT PRIMARY KEY,
    fullname VARCHAR(100) NOT NULL
    -- etc
);

CREATE TABLE tupleHolder
(   -- a tuple representing a necessary Three-some validation
    -- and vetting to get financing
    --
    -- If you can't vett these 3, you can't have my supercomputer financed
    --
    id INT AUTO_INCREMENT PRIMARY KEY,
    CEO INT NOT NULL,   -- Chief Executive Officer
    CFO INT NOT NULL,   -- Chief Financial Officer
    CIO INT NOT NULL,   -- Chief Geek
    creditWorthiness INT NOT NULL, -- 1 to 100. 100 is best

    -- the unique index is necessary for the device FK to succeed
    UNIQUE INDEX `idk_ContactTuple` (CEO,CFO,CIO), -- No duplicates ever. Good for re-use

    FOREIGN KEY `fk_th_ceo` (`CEO`) REFERENCES `contacts` (`id`),
    FOREIGN KEY `fk_th_cfo` (`CFO`) REFERENCES `contacts` (`id`),
    FOREIGN KEY `fk_th_cio` (`CIO`) REFERENCES `contacts` (`id`)
);

CREATE TABLE device
(   -- An Expensive Device, typically our Supercomputer that requires Financing.
    -- This device is so wildly expense we want to limit data changes
    --
    -- Note that the GRANTS (privileges) on this table are restricted.
    --
    id INT AUTO_INCREMENT PRIMARY KEY,
    something VARCHAR(100) NOT NULL,
    CEO INT NOT NULL,   -- Chief Executive Officer
    CFO INT NOT NULL,   -- Chief Financial Officer
    CIO INT NOT NULL,   -- Chief Geek
    FOREIGN KEY `fk_device_2_tuple` (`CEO` , `CFO` , `CIO`)
        REFERENCES `tupleHolder` (`CEO` , `CFO` , `CIO`)
    --
    -- Note that the GRANTS (privileges) on this table are restricted.
    --
);

DROP SCHEMA testRealLifeTuple;
Run Code Online (Sandbox Code Playgroud)

此模式的亮点归结为UNIQUE KEYintupleHolder表、FK in deviceGRANT限制(未显示授权)以及设备在 tupleHolder 中被屏蔽的事实,因为如上所述:

  • 赠款
  • 必须尊重FK,所以tupleHolder不能乱来

如果tupleHolder弄乱了(3 个contactsid),那么 FK 将被违反。

所述另一种方式,它是NO WAY与具有基于在设备的单个列中的FK的装置中,把它称为[device.badIdea INT],这将FK回tupleHolder.id。

此外,如前所述,这与仅具有contacts存在不同。相反,重要的是 的组成contacts存在,它是一个元组。在我们的例子中,元组已经过审查,并且具有信用评级,并且在购买设备后,该元组中的 id 不能被弄乱,除非有足够的 GRANTS 允许。即便如此,FK 已经到位。

这可能需要15分钟,要下沉,但有一个巨大的差异。

我希望这有帮助。