当两个表都是正确的选项时,将表与另外两个表链接起来的正确方法是什么

时间:2012-01-24 10:03:33

标签: mysql database database-design normalization

前言

我有三张桌子客户,个人和公司和客户可以是个人或公司,但不能同时是两者。我想找出关于连接这三个表的正确方法的一般意见。

示例

的详细信息

这三个表是“客户”,“公司”和“公司”。 “个人”和生成基本表的MySQL代码是:

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';

CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`customers`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`customers` (
  `CustomerID` INT NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(45) NULL ,
  PRIMARY KEY (`CustomerID`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`individuals`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`individuals` (
  `IndividualID` INT NOT NULL AUTO_INCREMENT ,
  `First Name` VARCHAR(45) NULL ,
  `Last Name` VARCHAR(45) NULL ,
  `DOB` DATE NULL ,
  PRIMARY KEY (`IndividualID`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`companies`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`companies` (
  `CompanyID` INT NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(60) NULL ,
  `StartedDate` DATE NULL ,
  `Address` VARCHAR(500) NULL ,
  PRIMARY KEY (`CompanyID`) )
ENGINE = InnoDB;


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

这是基本表,它们之间没有链接。我玩弄了几种连接表格的方法,但没有一种感觉正确。第一种方法是基本上发布一个“IndividualID”&客户表中的“CompanyID”和一个布尔值来说明它是哪一个但是这使得它可能已经填充并且没有办法用DB直接强化它也只是感觉不对就是这样:

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';

CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`companies`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`companies` (
  `CompanyID` INT NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(60) NULL ,
  `StartedDate` DATE NULL ,
  `Address` VARCHAR(500) NULL ,
  PRIMARY KEY (`CompanyID`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`individuals`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`individuals` (
  `IndividualID` INT NOT NULL AUTO_INCREMENT ,
  `First Name` VARCHAR(45) NULL ,
  `Last Name` VARCHAR(45) NULL ,
  `DOB` DATE NULL ,
  PRIMARY KEY (`IndividualID`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`customers`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`customers` (
  `CustomerID` INT NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(45) NULL ,
  `bIsCompany` TINYINT(1) NOT NULL ,
  `IndividualID` INT NULL ,
  `CompanyID` INT NULL ,
  PRIMARY KEY (`CustomerID`) ,
  INDEX `Customer_Company` (`CompanyID` ASC) ,
  INDEX `Customer_Individual` (`IndividualID` ASC) ,
  CONSTRAINT `Customer_Company`
    FOREIGN KEY (`CompanyID` )
    REFERENCES `mydb`.`companies` (`CompanyID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `Customer_Individual`
    FOREIGN KEY (`IndividualID` )
    REFERENCES `mydb`.`individuals` (`IndividualID` )
    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;

另一种方法是在客户和其他两个连接它们的表之间添加两个表,这些表感觉更好但不完美,因为您可以同时在两个表中都有链接。看起来像这样:

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';

CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`customers`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`customers` (
  `CustomerID` INT NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(45) NULL ,
  PRIMARY KEY (`CustomerID`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`individuals`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`individuals` (
  `IndividualID` INT NOT NULL AUTO_INCREMENT ,
  `First Name` VARCHAR(45) NULL ,
  `Last Name` VARCHAR(45) NULL ,
  `DOB` DATE NULL ,
  PRIMARY KEY (`IndividualID`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`companies`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`companies` (
  `CompanyID` INT NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(60) NULL ,
  `StartedDate` DATE NULL ,
  `Address` VARCHAR(500) NULL ,
  PRIMARY KEY (`CompanyID`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`company_customer`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`company_customer` (
  `CustomerID` INT NOT NULL ,
  `CompanyID` INT NOT NULL ,
  PRIMARY KEY (`CustomerID`, `CompanyID`) ,
  INDEX `CompanyCustomer_CompanyID` (`CompanyID` ASC) ,
  INDEX `CompanyCustomer_CustomerID` (`CustomerID` ASC) ,
  CONSTRAINT `CompanyCustomer_CompanyID`
    FOREIGN KEY (`CompanyID` )
    REFERENCES `mydb`.`companies` (`CompanyID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `CompanyCustomer_CustomerID`
    FOREIGN KEY (`CustomerID` )
    REFERENCES `mydb`.`customers` (`CustomerID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`individual_customer`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`individual_customer` (
  `IndividualID` INT NOT NULL ,
  `CompanyID` INT NOT NULL ,
  PRIMARY KEY (`IndividualID`, `CompanyID`) ,
  INDEX `CompanyCustomer_CompanyID` (`IndividualID` ASC) ,
  INDEX `CompanyCustomer_CustomerID` (`IndividualID` ASC) ,
  CONSTRAINT `IndividualCustomer_CompanyID0`
    FOREIGN KEY (`IndividualID` )
    REFERENCES `mydb`.`individuals` (`IndividualID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `IndividualCustomer_CustomerID0`
    FOREIGN KEY (`IndividualID` )
    REFERENCES `mydb`.`customers` (`CustomerID` )
    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;

正如我所提到的,最后一个选项是我喜欢的方法,但它仍然感觉不正确,仍然可能导致问题。 SO:

的问题:

  • 还有其他选择
  • 对所述选项的一般意见是什么,我错过了这些选项的任何优点/缺点。

提前致谢。

3 个答案:

答案 0 :(得分:2)

就我个人而言,我会根据OO原则寻找一些东西。

客户:将是超类。它将拥有公司和个人共同的所有元素。即Id,Name(可能是主地址表的主地址或ID,以及任何客户共有的任何其他内容)加上“Type”字段(例如:“IND”,“COM”)。

个人:这有一个客户的外键和所有特定于个人的字段,如中间名,DOB等。外键也是唯一的。

公司:相同 - 客户和公司特定字段的外键。外键也是唯一的。

理论上可以同时指向同一Customer条目的Individual和Company记录,但Type字段将有助于确保您始终可以在客户被视为个人或公司的情况下编写查询,并且从来没有。

示例:

    Select * from Individuals where exists 
    (select 'x" from Customers 
                where customer-id=individual-id and type="IND").

------------------ UPDATE:

您始终可以为这两种类型的客户创建视图,以缓解拆分问题:

Select Individuals.id, 
       Individuals.field1, 
       Individuals.field2, 
       Customer.FieldX, Customer.FieldY 
 from Individuals ind, Customers Cus 
 where ind.id=cus.id and cus.type="IND" 

是一种“重建”个人客户从两个表中获取字段的方法。这允许您作为一个整体访问它,至少在阅读它时。

实际上,我不确定在两个不同的表上拆分字段会出现什么问题。

答案 1 :(得分:0)

将所有常用字段放入Customer表。 CompanyPerson表只包含特定于每个列的列。 Customer.CustomerType是鉴别者。

enter image description here

也可以看一些 similar questions

答案 2 :(得分:0)

您真的需要中间表“individual_customer”和“company_customer”吗?这些是真正的m:n关系,意味着客户可以与多个公司或个人相关(反之亦然,公司与多个客户相关)?或者它只是1:1?

如果是1:1,您可以删除中间表并将“CustomerID”列添加到“个人”和“公司”表中。这将解决您的“无法将常见数据移动到客户表”的问题。并将CustomerID添加到主键或新的唯一键以防止这些表中的重复条目。

客户最终成为公司和个人的可能性仍然存在,但这是一个你不会轻易解决的问题,除非你按照p.marino和Damir的建议修改表格。只要数据存储在多个表中,您就无法在mySQL中使用数据库中的简单主键或唯一键强制执行该要求。只有应用程序逻辑或存储的函数/过程才能做到这一点。