【问题标题】:Is it possible to reference one column as multiple foreign keys?是否可以将一列引用为多个外键?
【发布时间】:2023-03-30 19:57:01
【问题描述】:

我的表很少,我想将 PDF 表中的一列引用到其他多个表。

例如,如果 PDF 表 select 输出如下所示:

ITEM_TYPE  ITEM_ID  QUANTITY

1          23       3
2          12       1

它告诉我:

PDF 上面有 3 个车轮产品和 1 个汽车模板标题;

我写了SQL代码,但是不能正常工作:

CREATE TABLE `pdf_created` (
    `id` INT(10) UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT,
    `pdf_id` INT(10) NOT NULL,
    `item_type` INT(3) UNSIGNED NOT NULL,
    `item_id` INT(10) UNSIGNED NOT NULL,
    `quantity` INT(3) NOT NULL,
    PRIMARY KEY (`id`),
    KEY `FK_pdf_id` (`pdf_id`),
    CONSTRAINT `FK_pdf_id` FOREIGN KEY (`pdf_id`) REFERENCES `pdf` (`id`),
    KEY `FK_item_type` (`item_type`),
    CONSTRAINT `FK_item_type` FOREIGN KEY (`item_type`) REFERENCES `item` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    KEY `FK_item_id` (`item_id`),
    CONSTRAINT `FK_item_id` FOREIGN KEY (`item_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT `FK_item_id` FOREIGN KEY (`item_id`) REFERENCES `service` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT `FK_item_id` FOREIGN KEY (`item_id`) REFERENCES `header` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `header` (
    `id` INT(10) UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT,
    `title` VARCHAR(255),
    `desc` VARCHAR(65535),
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `service` (
    `id` INT(10) UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT,
    `desc` VARCHAR(65535) NOT NULL,
    `price` DECIMAL(5,2) NOT NULL,
    `active` INT(1) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `product` (
    `id` INT(10) UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT,
    `category_id` INT(3) UNSIGNED NOT NULL,
    `symbol` VARCHAR(255),
    `desc` VARCHAR(65535), 
    `price` DECIMAL(5,2) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

可以创建吗?

【问题讨论】:

  • 不,这是不可能的。你的外键必须指向一个表,它不能用外键指向 table1、table2 或 table3。如果其中一个表中存在插入前和更新前触发器,您可以签入它
  • Michael,令人惊讶的是,在某些 DBMS 中可以在相同字段上定义 N>1 个外键并将它们指向不同的表。我刚刚在 MySQL 5.5、Oracle 11g 和 MS SQL 2012 中验证了这一点。我认为使用该功能几乎总是一个坏主意,但这并非不可能(并且在多个表被引用为的某些情况下可能很有用parent 都是相同的逻辑实体)。见stackoverflow.com/questions/19057188/…

标签: mysql sql foreign-keys


【解决方案1】:

应该是可以的。一个潜在的问题是您的三个外键约束具有相同的名称。

【讨论】:

    【解决方案2】:

    不,一个外键字段意味着引用一个表。

    如果您确实有您描述的 FK 约束,则 item_id 字段将引用所有三个表中的相同主键值。三个不同表中所需的主键很可能具有不同的主键。

    您想要的是让一条记录(行)引用表 Product、Header 和 Service 中的记录。这样做的方法是使用三个不同的字段,每个字段对应一个外键。

    我还注意到 Item 表具有 三个所需的外键。您可以让 PDF 表有一个引用 Item 的字段,而 Item 中的记录引用其他三个表。

    【讨论】:

    • 我想过,但这意味着总会有两列(产品、标题、服务)为空。
    • 没有。 Product 的一个外键将具有 Product 表中主键的值。一个引用 Header 的外键字段将具有 Header 表中主键的值。一个引用 Service 的外键字段将具有 Service 中主键字段的值。考虑 PDF 是否使用 Product 中的主键为 33、Header 中的主键为 57、Service 中的主键为 82 的记录。您不能将这三个主键值存储在一个字段中。
    • 好的,但是这个表有Quantity列,所以这个项目不能放在一行
    • PDF 表应该有列 FK_ITEM_TYPE、FK_PRODUCT_ID、FK_HEADER_ID、FK_SERVICE_ID 和 QUANTITY。如果表格上有这五个字段,为什么不能“此项目不能放在一行”?
    • 想一想:如果你有一个包含这些列的表,那么列'quantity'的值将指代什么?标头、产品还是服务?
    【解决方案3】:

    没有。

    也就是说,您不能以这种方式创建外键约束。但是,您可以使用没有外键约束的外键。

    外键是另一个表(或同一个表中的另一个记录)主键的值,可以在连接中使用。事实上,您可以引用主键以外的字段,如果您只需要使用连接的值。

    但是,外键约束告诉数据库强制执行规则,即对于表中的每个外键值,被引用的表都有一条记录作为它的主键。强制 PDF 表中的每个外键都有一个主键 IN ALL FOUR TABLES 对您不起作用。所以继续使用该字段来引用其他记录,但不要创建任何外键约束。

    【讨论】:

    • 另请注意,UML 和更标准的图表组织图表方法是将虚线框作为可能表的父级,将“箭头”和空三角形作为箭头头点从可能的表格到虚线框,并有一个从 PDF 表格到虚线框的正常箭头。
    • 好的,现在看起来像:CREATE TABLE pdf_created` (id INT(10) UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT, pdf_id INT(10) UNSIGNED NOT NULL, item_type INT( 3) UNSIGNED NOT NULL, item_id INT(10) UNSIGNED NOT NULL, quantity INT(5) UNSIGNED NOT NULL, 主键(id), 外键(pdf_id) 参考pdf (@987654330) @), 外键 (item_type) 参考 item (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`
    • 我不确定您是否想要或需要“FOREIGN KEY (pdf_id) REFERENCES pdf (id)”,否则看起来不错。
    【解决方案4】:

    它可能的外键约束名称应该像这样不同 并且主键和外键表列应该具有相同的数据类型。..

    CREATE TABLE `neo_address_t` (
      `address_id` varchar(8) NOT NULL,
      `address_line_1` varchar(45) NOT NULL,
      `address_line_2` varchar(45) NOT NULL,
      `address_line_3` varchar(45) NOT NULL,
      `address_city` varchar(45) NOT NULL,
      `address_zipcode` varchar(45) NOT NULL,
      `address_state` varchar(45) NOT NULL DEFAULT 'Karnataka',
      `address_country` varchar(45) NOT NULL DEFAULT 'INDIA',
      `created_by` varchar(8) DEFAULT NULL,
      `created_on` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      `last_modified_by` varchar(8) DEFAULT NULL,
      `last_modified_date` timestamp NULL DEFAULT '0000-00-00 00:00:00',
      `Refer_ID` int(11) DEFAULT NULL,
      `a_id` varchar(255) DEFAULT NULL,
      `referenceid` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`address_id`),
      KEY `hospital_ID_FK_idx` (`Refer_ID`),
      CONSTRAINT `Patient_ID_FK` FOREIGN KEY (`Refer_ID`) REFERENCES `neo_patient_t` (`patient_ID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
      CONSTRAINT `hospital_ID_FK` FOREIGN KEY (`Refer_ID`) REFERENCES `neo_hospital_t` (`hospital_id`) ON DELETE NO ACTION ON UPDATE CASCADE,
      CONSTRAINT `staff_ID_FK` FOREIGN KEY (`Refer_ID`) REFERENCES `neo_staff_t` (`staff_ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
    )     
    ENGINE=InnoDB DEFAULT CHARSET=utf8$$
    

    【讨论】:

      【解决方案5】:

      你遇到的问题叫Polymorphic Associations

      请参考这个问题:MySQL - Conditional Foreign Key Constraints

      【讨论】:

        【解决方案6】:

        理论上,您不能在单个列上强制执行多个外键。 或者,您可以使用验证多个表中存在的输入并执行必要操作的过程来强制执行此操作。 请注意,该特定表上的所有操作都应由验证所需条件的过程完成,否则会导致违反完整性。

        【讨论】:

          【解决方案7】:

          是的,这是可能的。即使看起来有点奇怪。

          首先我想展示我的 MySQL Workbench 的屏幕截图以证明它可以工作。

          ...对于那些说“也许您忘记应用更改”的人...这是架构浏览器的屏幕截图:

          最后是带有一些示例数据的导出转储:

          -- MySQL dump 10.13  Distrib 5.7.12, for Win64 (x86_64)
          --
          -- Host: localhost    Database: multiple_foreign_keys
          -- ------------------------------------------------------
          -- Server version   5.7.17-log
          
          /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
          /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
          /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
          /*!40101 SET NAMES utf8 */;
          /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
          /*!40103 SET TIME_ZONE='+00:00' */;
          /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
          /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
          /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
          /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
          
          --
          -- Table structure for table `header`
          --
          
          DROP TABLE IF EXISTS `header`;
          /*!40101 SET @saved_cs_client     = @@character_set_client */;
          /*!40101 SET character_set_client = utf8 */;
          CREATE TABLE `header` (
            `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
            `title` varchar(255) DEFAULT NULL,
            `desc` varchar(255) DEFAULT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `id` (`id`)
          ) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8;
          /*!40101 SET character_set_client = @saved_cs_client */;
          
          --
          -- Dumping data for table `header`
          --
          
          LOCK TABLES `header` WRITE;
          /*!40000 ALTER TABLE `header` DISABLE KEYS */;
          INSERT INTO `header` VALUES (42,'Header','Test Header');
          /*!40000 ALTER TABLE `header` ENABLE KEYS */;
          UNLOCK TABLES;
          
          --
          -- Table structure for table `pdf_created`
          --
          
          DROP TABLE IF EXISTS `pdf_created`;
          /*!40101 SET @saved_cs_client     = @@character_set_client */;
          /*!40101 SET character_set_client = utf8 */;
          CREATE TABLE `pdf_created` (
            `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
            `pdf_id` int(10) NOT NULL,
            `item_type` int(3) unsigned NOT NULL,
            `item_id` int(10) unsigned NOT NULL,
            `quantity` int(3) NOT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `id` (`id`),
            KEY `fk_item_to_product_idx` (`item_id`),
            CONSTRAINT `fk_item_to_header` FOREIGN KEY (`item_id`) REFERENCES `header` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
            CONSTRAINT `fk_item_to_product` FOREIGN KEY (`item_id`) REFERENCES `product` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
            CONSTRAINT `fk_item_to_service` FOREIGN KEY (`item_id`) REFERENCES `service` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
          ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
          /*!40101 SET character_set_client = @saved_cs_client */;
          
          --
          -- Dumping data for table `pdf_created`
          --
          
          LOCK TABLES `pdf_created` WRITE;
          /*!40000 ALTER TABLE `pdf_created` DISABLE KEYS */;
          INSERT INTO `pdf_created` VALUES (1,2,5,42,1);
          /*!40000 ALTER TABLE `pdf_created` ENABLE KEYS */;
          UNLOCK TABLES;
          
          --
          -- Table structure for table `product`
          --
          
          DROP TABLE IF EXISTS `product`;
          /*!40101 SET @saved_cs_client     = @@character_set_client */;
          /*!40101 SET character_set_client = utf8 */;
          CREATE TABLE `product` (
            `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
            `category_id` int(3) unsigned NOT NULL,
            `symbol` varchar(255) DEFAULT NULL,
            `desc` varchar(255) DEFAULT NULL,
            `price` decimal(5,2) NOT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `id` (`id`)
          ) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8;
          /*!40101 SET character_set_client = @saved_cs_client */;
          
          --
          -- Dumping data for table `product`
          --
          
          LOCK TABLES `product` WRITE;
          /*!40000 ALTER TABLE `product` DISABLE KEYS */;
          INSERT INTO `product` VALUES (42,13,'product','desc',10.00);
          /*!40000 ALTER TABLE `product` ENABLE KEYS */;
          UNLOCK TABLES;
          
          --
          -- Table structure for table `service`
          --
          
          DROP TABLE IF EXISTS `service`;
          /*!40101 SET @saved_cs_client     = @@character_set_client */;
          /*!40101 SET character_set_client = utf8 */;
          CREATE TABLE `service` (
            `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
            `desc` varchar(255) NOT NULL,
            `price` decimal(5,2) NOT NULL,
            `active` int(1) NOT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `id` (`id`)
          ) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8;
          /*!40101 SET character_set_client = @saved_cs_client */;
          
          --
          -- Dumping data for table `service`
          --
          
          LOCK TABLES `service` WRITE;
          /*!40000 ALTER TABLE `service` DISABLE KEYS */;
          INSERT INTO `service` VALUES (42,'some service',5.00,1);
          /*!40000 ALTER TABLE `service` ENABLE KEYS */;
          UNLOCK TABLES;
          /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
          
          /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
          /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
          /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
          /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
          /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
          /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
          /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
          
          -- Dump completed on 2018-08-27 10:31:41
          

          如果您想测试它而不是 SQL 脚本编辑器,请在 MySQL Workbench 中使用“数据导入/恢复”。

          但是您不应该对pdf_created.item_id 引用的productheaderservice 表使用自动增量,因为如果这三个表同时使用这将是一个幸运的巧合将获得相同的 id。您应该分配 id(项目的 id)。

          【讨论】:

            【解决方案8】:

            是的,这是可能的,您可以将一列用作多个表的外键,但这也不是一个好习惯,因为您永远不会在需要使用这种方法的地方如此依赖,而是为了学习你可以使用这种类型的 SQL 查询来完成这项工作;)

            ALTER TABLE demo4 ADD CONSTRAINT rel1 FOREIGN KEY (two) 引用 demo1(id) ON DELETE RESTRICT ON UPDATE RESTRICT;

            ALTER TABLE demo4 ADD CONSTRAINT rel2 FOREIGN KEY (two) 引用 demo2(id) ON DELETE RESTRICT ON UPDATE RESTRICT;

            ALTER TABLE demo4 ADD CONSTRAINT rel3 FOREIGN KEY (two) 引用 demo3(id) ON DELETE RESTRICT ON UPDATE RESTRICT;

            【讨论】:

              猜你喜欢
              • 2016-04-02
              • 1970-01-01
              • 2018-10-26
              • 1970-01-01
              • 1970-01-01
              • 2014-08-27
              • 2017-01-04
              • 2014-12-15
              • 1970-01-01
              相关资源
              最近更新 更多