【问题标题】:Enforcing single subtype SQL table for supertype table为超类型表强制执行单个子类型 SQL 表
【发布时间】:2016-05-25 16:04:08
【问题描述】:

我有一个名为“vehicles”的超类型表。我还有三个子类型表,称为“飞机”、“汽车”和“自行车”,这些子类型表中只有一个必须链接到车辆超类型表(或者换句话说,必须使用车辆主键 ID作为其主键 ID)。

应如何建模以强制执行此行为?

编辑 Mike Brant 推荐的拟议架构。

-- MySQL Script generated by MySQL Workbench
-- 05/25/16 09:20:17
-- Model: New Model    Version: 1.0
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`vehicle_types`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`vehicle_types` (
  `type` CHAR(8) NOT NULL,
  PRIMARY KEY (`type`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`vehicles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`vehicles` (
  `idvehicles` INT NOT NULL AUTO_INCREMENT,
  `type` CHAR(8) NOT NULL,
  `data` VARCHAR(45) NULL,
  PRIMARY KEY (`idvehicles`),
  INDEX `fk_vehicles_vehicle_types_idx` (`type` ASC),
  CONSTRAINT `fk_vehicles_vehicle_types`
    FOREIGN KEY (`type`)
    REFERENCES `mydb`.`vehicle_types` (`type`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`airplanes`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`airplanes` (
  `vehicles_idvehicles` INT NOT NULL,
  `data_for_airplanes` VARCHAR(45) NULL,
  PRIMARY KEY (`vehicles_idvehicles`),
  CONSTRAINT `fk_airplanes_vehicles1`
    FOREIGN KEY (`vehicles_idvehicles`)
    REFERENCES `mydb`.`vehicles` (`idvehicles`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`automobiles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`automobiles` (
  `vehicles_idvehicles` INT NOT NULL,
  `data_for_automobiles` VARCHAR(45) NULL,
  PRIMARY KEY (`vehicles_idvehicles`),
  CONSTRAINT `fk_automobiles_vehicles1`
    FOREIGN KEY (`vehicles_idvehicles`)
    REFERENCES `mydb`.`vehicles` (`idvehicles`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`bicycles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`bicycles` (
  `vehicles_idvehicles` INT NOT NULL,
  `data_for_bicycles` VARCHAR(45) NULL,
  PRIMARY KEY (`vehicles_idvehicles`),
  CONSTRAINT `fk_bicycles_vehicles1`
    FOREIGN KEY (`vehicles_idvehicles`)
    REFERENCES `mydb`.`vehicles` (`idvehicles`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

【问题讨论】:

  • 在此处需要更多信息以就架构提出建议。您可以从当前提议的架构开始吗?
  • @MikeBrant 请查看已编辑的原始问题。

标签: mysql sql database-design


【解决方案1】:

我认为插入车辆和任何其他子类型应该同时进行。因此,您可以获取车辆 ID 并将其作为新的子类型 PK ID。如果您同时拥有所有信息,您可以创建交易并先插入车辆记录,然后再插入子类型记录。然后提交你的事务,否则回滚它。如果您想知道哪个记录属于哪个子类型表记录,您可以在您的车辆表中添加一个列,例如可以有 3 个不同的值,例如 0、1、2,显示哪个记录属于哪个子表。 java中的枚举可以用在类似的地方,以便更清楚地说明

【讨论】:

  • 0,1,2 是否对应airplanes, automobiles, bicycles?是否应该使用任何额外的外键约束或触发器?
  • 是的,它们对应于您的子类型。不,不需要外键。但是您可以在您的车辆表上放置一个控件(检查),只应在此字段中插入 0,1 和 2。但是,如果您有许多具有此功能的表,您可以创建一个用于存储任何子类型值的表,并将其外键添加到您的表中,例如 Vehicle。
【解决方案2】:

查看您提出的架构,我认为您没有任何理由为每种车辆类型设置单独的表,甚至没有理由为每种车辆类型设置单独的表来包含允许的车辆类型。您的车辆特定表基本上都是相同的,这意味着您可以轻松地折叠成一个表,并且您可以使用 ENUM 字段来强制执行允许的车辆类型。

为什么不只有一个像下面这样的车辆表?

CREATE TABLE IF NOT EXISTS `mydb`.`vehicles` (
  `idvehicles` INT NOT NULL AUTO_INCREMENT,
  `type` ENUM('airplane', 'automobile', 'bicycle') NOT NULL,
  `data` VARCHAR(45) NULL,
  PRIMARY KEY (`idvehicles`),
  INDEX `fk_vehicles_vehicle_types_idx` (`type` ASC))
ENGINE = InnoDB;

这种方法完全消除了 5 个表中的 4 个,这意味着在对这些记录执行 CRUD 操作时,您不再需要考虑使用连接、外键约束等。

【讨论】:

  • Mike,对不起,我在每个子类型表中使用了相同的 data 列,但这并不意味着这三个子类型中的每一个都只有一个列。实际上,飞机、汽车和自行车的数据会非常不同,并且需要不同数量的列。
  • @user1032531 明白了。在这种情况下,您可能应该在原始问题中添加有关写入和检索此数据所需的预期调用模式的信息。您的应用程序需要使用这些数据的方式将决定哪种模式可能是最合适的。例如,您是否希望在任何时候都必须在一个列表中列出所有可用车辆,而不管其类型如何,还是只在单个车辆类型中进行查询?
  • 无论类型如何,我都需要列出所有车辆,并希望在此列表中显示该类型。类型“可以”通过检查连接到它的子类型表来确定,但“可能”应该包括一个额外的类型列以仅强制一种类型。此外,车辆将连接到其他表,这是我使用超类型/子类型设计的主要原因(另一个好处是保存公共数据)。
【解决方案3】:

要强制独占子类型,请将type 指示符复制到每个子类型表中并使用复合外键约束:

CREATE TABLE IF NOT EXISTS `mydb`.`airplanes` (
  `vehicles_idvehicles` INT NOT NULL,
  `type` CHAR(8) NOT NULL,
  `data_for_airplanes` VARCHAR(45) NULL,
  PRIMARY KEY (`vehicles_idvehicles`),
  CONSTRAINT `fk_airplanes_vehicles1`
    FOREIGN KEY (`vehicles_idvehicles`, `type`)
    REFERENCES `mydb`.`vehicles` (`idvehicles`, `type`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

接下来需要限制每个表中type 的值。不幸的是,MySQL 不支持检查约束,因此您需要使用触发器:

DELIMITER ;;

CREATE TRIGGER airplanes_insert_type_check
    BEFORE INSERT ON airplanes
    FOR EACH ROW
BEGIN 
    IF NEW.`type` != 'airplane' THEN 
        SIGNAL SQLSTATE '45000'
            SET MESSAGE_TEXT = 'Invalid type in airplanes';
    END IF; 
END;;

CREATE TRIGGER airplanes_update_type_check
    BEFORE UPDATE ON airplanes
    FOR EACH ROW
BEGIN 
    IF NEW.`type` != 'airplane' THEN 
        SIGNAL SQLSTATE '45000'
            SET MESSAGE_TEXT = 'Invalid type in airplanes';
    END IF; 
END;;

DELIMITER ;

因此,由于触发器限制,超类型表中的type 指示符将仅匹配子类型表的type 指示符之一,并将通过外键约束强制执行,防止子类型重叠。

【讨论】:

  • 感谢 reaanb,为什么要将 type 添加到子类型表而不仅仅是超类型表?
  • 我更新了我的答案。由于触发器限制,超类型中的类型指示符将仅匹配子类型表的类型指示符之一,并将通过外键约束强制执行,防止子类型重叠。
  • 啊,所以我原来的方法的问题是数据库允许多个子类型表连接到给定的超表记录?
  • 问题不在于连接。您可以不受约束地告诉子类型它们应该是什么。
  • 我确实有“并且只有一个这些子类型表必须链接到车辆超类型表”。看来您的触发器可能会强制执行此操作。
猜你喜欢
  • 2013-02-05
  • 2019-02-15
  • 2018-09-26
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-08
相关资源
最近更新 更多