【问题标题】:MYSQL - One Column Referenced to Multiple TableMYSQL - 引用多个表的一列
【发布时间】:2012-06-06 07:29:48
【问题描述】:

表中的单个列可以引用到多个表吗?

【问题讨论】:

  • 是否意味着该列可以作为外键被多个其他表引用,或者该列可以引用多个其他表,就像在单个列上的多个外键约束一样?
  • 没有什么可以阻止您创建多个外键约束。虽然我想不出为什么这是必要的
  • @Michael 是的,我的意思是“列可以引用多个其他表,如多个外键”。

标签: mysql sql database-design data-modeling referential-integrity


【解决方案1】:

一个很晚的答案,但对于那些想知道和谷歌的人来说。

是的这可以做到,但它不是好的做法,即使它很简单,它可能会在你的脸上炸毁 em> 如果你不是很清楚自己在做什么。不推荐。

但是,我可以看到用途。例如,您有一个包含数百万条记录的大表,并且您希望在特殊情况下链接到未知或多个表(在这种情况下最好是 许多)。对于多个表,如果您为所有表创建一个外键,那么您的数据库大小将是一个巨大的膨胀。例如,在技术支持系统中可能存在未知表,您希望链接到可能存在问题的表中的记录,这可能是(几乎)数据库中的所有表,包括未来的表。

当然,您需要 两个 字段来链接:一个外键字段和它所链接的表的名称。让我们称他们为foreignIdlinkedTable

linkedTable 可以是枚举或字符串,最好是枚举(更少的空间),但这只有在您要链接到的不同表是固定的情况下才有可能。

让我们举一个非常愚蠢的例子。您有一个巨大的用户表users,其中一些用户可以将一个个人数据集添加到他们的个人资料中。这可能是关于他们的爱好、宠物、他们从事的运动他们的职业。现在,此信息在所有四种情况下都不同。 (实际上 4 个可能的表不足以证明这种结构的合理性)

现在假设linkedTable 是一个枚举,它的可能值有petshobbiessportsprofessions,它们是四个不同结构表的名称。假设id 是所有四个中的 pkey。

你加入的例子如下:

SELECT * FROM users 
    LEFT JOIN  pets        ON linkedTable = 'pets'        AND foreignId = pets.id
    LEFT JOIN  hobbies     ON linkedTable = 'hobbies'     AND foreignId = hobbies.id
    LEFT JOIN  sports      ON linkedTable = 'sports'      AND foreignId = sports.id
    LEFT JOIN  professions ON linkedTable = 'professions' AND foreignId = professions.id

这只是一个基本的玩笑。由于您可能只在极少数情况下需要链接,因此当您循环访问用户(没有加入)时,您更有可能使用您的编程语言(如 PHP)进行查找。

想试试吗?您可以自己尝试构建此测试数据库(确保使用测试数据库):

CREATE TABLE IF NOT EXISTS `users` (
    `id` INT NOT NULL AUTO_INCREMENT , 
    `name` VARCHAR(100) NOT NULL , 
    `linkedTable` ENUM('pets','hobbies','sports','professions') NULL DEFAULT NULL , 
    `foreignId` INT NULL DEFAULT NULL , 
  PRIMARY KEY (`id`), INDEX (`linkedTable`)
) ;

CREATE TABLE  IF NOT EXISTS `pets` ( 
    `id` INT NOT NULL AUTO_INCREMENT , 
    `animalTypeId` INT NOT NULL , 
    `name` VARCHAR(100) NOT NULL , 
    `colorId` INT NOT NULL , 
  PRIMARY KEY (`id`), INDEX (`animalTypeId`), INDEX (`colorId`)
) ;

CREATE TABLE  IF NOT EXISTS `hobbies` ( 
    `id` INT NOT NULL AUTO_INCREMENT , 
    `hobbyTypeId` INT NOT NULL , 
    `hoursPerWeekSpend` INT NOT NULL , 
    `websiteUrl` VARCHAR(300) NULL , 
  PRIMARY KEY (`id`), INDEX (`hobbyTypeId`)
) ;

CREATE TABLE  IF NOT EXISTS `sports` ( 
    `id` INT NOT NULL AUTO_INCREMENT , 
    `sportTypeId` INT NOT NULL , 
    `hoursPerWeekSpend` INT NOT NULL , 
    `nameClub` VARCHAR(100) NULL , 
    `professional` TINYINT NOT NULL DEFAULT 0, 
  PRIMARY KEY (`id`), INDEX (`sportTypeId`)
) ;

CREATE TABLE  IF NOT EXISTS `professions` ( 
    `id` INT NOT NULL AUTO_INCREMENT , 
    `professionId` INT NOT NULL , 
    `hoursPerWeek` INT NOT NULL , 
    `nameCompany` VARCHAR(100) NULL , 
    `jobDescription` VARCHAR(400) NULL, 
  PRIMARY KEY (`id`), INDEX (`professionId`)
) ;


INSERT INTO `users` (`id`, `name`, `linkedTable`, `foreignId`) 
   VALUES 
   (NULL, 'Hank', 'pets', '1'), 
   (NULL, 'Peter', 'hobbies', '2'), 
   (NULL, 'Muhammed', 'professions', '1'), 
   (NULL, 'Clarice', NULL, NULL), 
   (NULL, 'Miryam', 'professions', '2'), 
   (NULL, 'Ming-Lee', 'hobbies', '1'), 
   (NULL, 'Drakan', NULL, NULL), 
   (NULL, 'Gertrude', 'sports', '2'), 
   (NULL, 'Mbase', NULL, NULL);


INSERT INTO `pets` (`id`, `animalTypeId`, `name`, `colorId`) 
VALUES (NULL, '1', 'Mimi', '3'), (NULL, '2', 'Tiger', '8');

INSERT INTO `hobbies` (`id`, `hobbyTypeId`, `hoursPerWeekSpend`, `websiteUrl`) 
VALUES (NULL, '123', '21', NULL), (NULL, '2', '1', 'http://www.freesoup.org');

INSERT INTO `sports` (`id`, `sportTypeId`, `hoursPerWeekSpend`, `nameClub`, `professional`) 
VALUES (NULL, '2', '3', 'Racket to Racket', '0'), (NULL, '12', '34', NULL, '1');

INSERT INTO `professions` (`id`, `professionId`, `hoursPerWeek`, `nameCompany`, `jobDescription`) 
VALUES (NULL, '275', '40', 'Ben & Jerry\'s', 'Ice cream designer'), (NULL, '21', '24', 'City of Dublin', 'Garbage collector');

然后运行第一个查询。

有趣的讨论笔记:如何索引这个?

【讨论】:

  • 嗨,@Roemer!你能告诉我更多关于为什么it is NOT good practice的细节吗?
  • 因为它使编码变得不必要的复杂并且它不是标准的,所以维护它的人必须确切地知道发生了什么。你永远不知道几年后谁会维护一些东西。即使是你自己,你也可能已经忘记了。您希望遵循标准做法,以便能够轻松地维护、扩展和编码。
  • 嗨,@Roemer,你能告诉我对于类似的事情,什么是最好的(正确的)方法而不是这种方式?
  • 嗨,Oscar,只需为每个连接使用单独的外键。或者,如果您有数百万条记录并且不希望其中有很多列(例如这个外国 id),如果这些列中的许多通常是空的,请在“中间”使用一个单独的表。您将其 1 对 1 链接到主表。您甚至可以使用相同的主键(相同的 pkey 名称、相同的 id)来链接它。在这个单独的表中拥有这些外国 id 和可能更不常用的列,这些列将有更少的记录。这样您就可以拥有一个包含 1 亿条记录的主表和一个只有 5000 条记录的额外表。只需在需要时加入它们即可。
【解决方案2】:

如果您的意思是“一个表中的列可以用作多个表中的外键”,那么答案是YES。这就是关系数据库的重点

【讨论】:

  • 我的意思是“一列可以引用多个其他表”
【解决方案3】:

是的,您可以这样做。这是一个关于如何做到这一点的示例:

这是一个包含一个列(CountryID)的表,该列将被多个表引用:

CREATE TABLE DLAccountingSystem.tblCountry
(
    CountryID       INT             AUTO_INCREMENT NOT NULL PRIMARY KEY,
    CountryName     VARCHAR(128)    NOT NULL,
    LastEditUser    VARCHAR(128)    NOT NULL,
    LastEditDate    DATETIME        NOT NULL
) ENGINE=INNODB;

这里是要引用列(CountryID)的表:

CREATE TABLE DLAccountingSystem.tblCity
(
    CityID          INT             AUTO_INCREMENT NOT NULL PRIMARY KEY,
    CountryID       INT             NOT NULL,
    CityName        VARCHAR(128)    NOT NULL,
    LastEditUser    VARCHAR(128)    NOT NULL,
    LastEditDate    DATETIME        NOT NULL
) ENGINE=INNODB;

CREATE TABLE DLAccountingSystem.tblProvince
(
    ProvinceID      INT             AUTO_INCREMENT NOT NULL PRIMARY KEY,
    CountryID       INT             NOT NULL,
    ProvinceName    VARCHAR(128)    NOT NULL,
    LastEditUser    VARCHAR(128)    NOT NULL,
    LastEditDate    DATETIME        NOT NULL
) ENGINE=INNODB;

以下是创建对列的引用的方法:

 ALTER TABLE DLAccountingSystem.tblCity
 ADD CONSTRAINT fk_tblcitycountryid FOREIGN KEY CountryID (CountryID)
 REFERENCES DLAccountingSystem.tblCountry (CountryID)
 ON DELETE NO ACTION
 ON UPDATE NO ACTION

 ALTER TABLE DLAccountingSystem.tblProvince
 ADD CONSTRAINT fk_tblprovincecountryid FOREIGN KEY CountryID (CountryID)
 REFERENCES DLAccountingSystem.tblCountry (CountryID)
 ON DELETE NO ACTION
 ON UPDATE NO ACTION

这是一个表,它的列引用了(CountryID、ProvinceID、CityID)多个表中的不同列(我个人不建议这种表结构方式。我认为没有冒犯;))

CREATE TABLE DLAccountingSystem.tblPersons
(
    PersonID       INT          AUTO_INCREMENT NOT NULL PRIMARY KEY,
    PlaceID        INT          NOT NULL,
    PlaceTypeID    INT          NOT NULL, -- this property refers to what table are you referencing.
 //Other properties here.....
) ENGINE=INNODB;

您还应该有一个包含 PlaceType 的查找表:

CREATE TABLE DLAccountingSystem.tblPlaceType
(
    PlaceTypeID       INT          AUTO_INCREMENT NOT NULL PRIMARY KEY,
    PlaceTypeName        INT          NOT NULL
 //Other properties here.....
) ENGINE=INNODB;

这是获取它的方法:

SELECT p1.PersonID,
       tcity.CityName,
       tprov.ProvinceName,
       tcoun.CountryName
FROM DLAccountingSystem.tblPersons p1 
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.CityName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblCity c ON p2.ObjectID = c.CityID WHERE PlaceTypeID = @CityTypeID) tcity ON p1.PersonID = tcity.PersonID
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.ProvinceName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblProvince c ON p2.ObjectID = c.ProvinceID WHERE PlaceTypeID = @ProvinceTypeID) tprov ON p1.PersonID = tprov.PersonID
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.CountryName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblCountry c ON p2.ObjectID = c.CountryID WHERE PlaceTypeID = @CountryTypeID) tcoun ON p1.PersonID = tcoun.PersonID

您可以从其他表格中选择,例如

【讨论】:

  • 我的意思是“一列可以引用多个其他表”
  • 是的,这也是可能的。但是在实现它时要保持关系完整性是非常困难的。我们的数据库中有类似的东西用于通知。你能给我确切的实体集吗?
【解决方案4】:

同一列或一组列可以充当一个或多个外键的父端点和/或子端点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-21
    • 1970-01-01
    • 2015-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-26
    相关资源
    最近更新 更多