MySQL数据库复合fk设计考虑

时间:2017-10-25 06:52:26

标签: mysql database-design

首先,这是我在这里的第一篇文章,但多年来这个网站一直是#34;这个地方"当我需要回答我的问题。但是,我在MySQL数据库设计问题上遇到了麻烦,我似乎无法找到答案或解决自己。

此处发布了所涉及表格的代码片段(Workbench已生成)。问题出现在

之后
CREATE TABLE IF NOT EXISTS `TestCenter`.`test` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `report_number` VARCHAR(10) GENERATED ALWAYS AS (CONCAT(YEAR(create_time), '-',  seq_number)) VIRTUAL,
  `seq_number` INT(5) ZEROFILL NOT NULL,
  `test_state_id` TINYINT NOT NULL DEFAULT 1,
  `customer_id` BIGINT NOT NULL,
  `sample_rate` INT NOT NULL,
  `comment` VARCHAR(255) NULL,
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `start_time` DATETIME NULL,
  `end_time` DATETIME NULL,
  PRIMARY KEY (`id`),
  INDEX `fk_idx_test_customer_id` (`customer_id` ASC),
  INDEX `fk_idx_test_test_state_id` (`test_state_id` ASC),
  INDEX `idx_test_start_and_end_time` (`start_time` ASC, `end_time` ASC),
  UNIQUE INDEX `report_number_UNIQUE` (`report_number` ASC),
  CONSTRAINT `fk_test_customer_id`
    FOREIGN KEY (`customer_id`)
    REFERENCES `TestCenter`.`customer` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_test_test_state_id`
    FOREIGN KEY (`test_state_id`)
    REFERENCES `TestCenter`.`test_state` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `TestCenter`.`machine` (
  `id` TINYINT NOT NULL AUTO_INCREMENT,
  `description` VARCHAR(45) NOT NULL,
  `is_controlled` TINYINT(1) NOT NULL DEFAULT 0,
  `active` TINYINT(1) NOT NULL DEFAULT 1,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `TestCenter`.`motion` (
  `id` TINYINT NOT NULL,
  `description` VARCHAR(45) NOT NULL,
  `plc_value` TINYINT NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `TestCenter`.`machine_motion` (
  `machine_id` TINYINT NOT NULL,
  `motion_id` TINYINT NOT NULL,
  `active` TINYINT(1) NOT NULL DEFAULT 1,
  INDEX `fk_idx_machine_motion_motion_id` (`motion_id` ASC),
  PRIMARY KEY (`machine_id`, `motion_id`),
  CONSTRAINT `fk_machine_motion_machine_id`
    FOREIGN KEY (`machine_id`)
    REFERENCES `TestCenter`.`machine` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_machine_motion_motion_id`
    FOREIGN KEY (`motion_id`)
    REFERENCES `TestCenter`.`motion` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `TestCenter`.`test_step` (
  `step_number` INT NOT NULL,
  `test_id` BIGINT NOT NULL,
  `machine_id` TINYINT NOT NULL,
  `motion_id` TINYINT NOT NULL,
  `step_state_id` TINYINT NOT NULL DEFAULT 1,
  `auto_continue` TINYINT(1) NOT NULL DEFAULT 0,
  `start_time` DATETIME NULL,
  `end_time` DATETIME NULL,
  INDEX `fk_idx_test_step_machine_and motion_id` (`machine_id` ASC, `motion_id` ASC),
  PRIMARY KEY (`test_id`, `step_number`),
  INDEX `fk_idx_test_step_step_state_id` (`step_state_id` ASC),
  INDEX `fk_idx_test_step_test_id` (`test_id` ASC, `machine_id` ASC),
  CONSTRAINT `fk_test_step_machine_and motion_id`
    FOREIGN KEY (`machine_id` , `motion_id`)
    REFERENCES `TestCenter`.`machine_motion` (`machine_id` , `motion_id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_test_step_test_id`
    FOREIGN KEY (`test_id`)
    REFERENCES `TestCenter`.`test` (`id`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  CONSTRAINT `fk_test_step_step_state_id`
    FOREIGN KEY (`step_state_id`)
    REFERENCES `TestCenter`.`test_state` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)

来自EER图Image of involved tables

的相同表格的图像

我的问题是' machine_id'在' test_step'表(见图),它真的不属于那里,应该在“测试”中。作为单个测试的表由同一台机器完成,无论进行测试涉及多少步骤。但是我也想在' test_step'中使用fk约束。表只允许有效的' machine_motion'组合

正如我所看到的,一个解决方案是,拥有' machine_id'在'测试'表格,但这将引入一个循环参考作为测试'然后桌子会引用机器'表

另一个解决方案是,从' test_step'中删除fk约束。表,移动' machine_id'进入测试'表并将“fk”约束添加到' motion_id'在' test_step'表引用'动作'表

实际上,在写这篇文章时,我发现解决方案2可能是要走的路,但是,

我错过了什么吗?我真的很感激一些投入,因为我现在几天都在努力。

1 个答案:

答案 0 :(得分:1)

在现有设计中:

  • 每个测试可能有多个测试步骤;每个测试步骤都属于一个测试。

  • 每项测试可由多台机器执行。

因此,无论是谁设计的,都希望不止一台机器可以参与一次测试。这可能是当时的要求。

您的陈述

  

...单一测试由同一台机器完成..

更改了这些内容,并且引入了可以用语言表达的约束

  • 每个测试都由一台机器完成,对于每台机器,该机器可以进行多项测试。

  • 对于每个测试步骤,该步骤由分配给测试步骤所属的测试的机器执行。

因为你基本上引入了新的约束,你应该:

  1. machine_id添加到test表格;
  2. machine_id表格向test表格添加FK {machine};
  3. 将AK(唯一){id, machine_id}添加到test表格;
  4. test_id, machine_id表格向test_step表格添加FK {test};
  5. 您现在可以从test_id表格中删除冗余FK {test_step}到test
  6. 这样,如果需求再次发生变化,您可以简单地删除这四个DB对象。 可能是整个测试系统将来可能会扩展,原始要求可能仍然存在。

    test {
        id
      , report_number
      , seq_number
      , test_state_id
      , customer_id
      , sample_rate
      , comment
      , create_time
      , start_time
      , end_time
    
      , machine_id -- added, point 1
    }
    PK  {id}
    AK1 {report_number}
    AK2 {id, machine_id} -- added, point 3
    
    FK1 {customer_id}   REFERENCES customer   {id}
    FK2 {test_state_id} REFERENCES test_state {id}
    FK3 {machine_id}    REFERENCES machine    {id} -- added, point 2
    
    
    
    machine {
        id
      , description
      , is_controlled
      , active
    }
    PK {id}
    
    
    motion {
        id
      , description
      , plc_value
    }
    PK {id}
    
    
    
    machine_motion {
        machine_id
      , motion_id
      , active
      }
    PK  {machine_id, motion_id}
    
    FK1 {machine_id} REFERENCES machine {id}
    FK2 {motion_id}  REFERENCES motion  {id}
    
    
    
    test_step {
        step_number
      , test_id
      , machine_id
      , motion_id
      , step_state_id
      , auto_continue
      , start_time
      , end_time
      }
    PK  {test_id, step_number}
    
    FK1 {machine_id, motion_id} REFERENCES machine_motion {machine_id, motion_id}
    FK2 {test_id}               REFERENCES test           {id} -- redundant, pt 5
    FK3 {step_state_id}         REFERENCES step_state     {id}
    
    FK4 {test_id, machine_id} REFERENCES test {id, machine_id} -- added, pt 4