MySQL Structure Join表两个可选约束

时间:2012-09-04 21:11:51

标签: mysql

我有三张桌子:

地址

CREATE  TABLE IF NOT EXISTS `main`.`address` (  
  `id` BIGINT NOT NULL AUTO_INCREMENT ,  
  `street_number` VARCHAR(5) NOT NULL ,  
  `street_name` VARCHAR(255) NOT NULL ,  
  `town_village` VARCHAR(50) NOT NULL ,  
  `county` VARCHAR(50) NOT NULL ,  
  `country` VARCHAR(45) NOT NULL ,  
  `postcode` VARCHAR(10) NOT NULL ,  
  PRIMARY KEY (`id`) ,  
  UNIQUE INDEX `street_number_UNIQUE` (`street_number` ASC, `street_name` ASC, `town_village` ASC,   `county` ASC, `country` ASC, `postcode` ASC) )  
ENGINE = InnoDB  

**Geolocation**  
CREATE  TABLE IF NOT EXISTS `warrington_main`.`address` (  
  `id` BIGINT NOT NULL AUTO_INCREMENT ,  
  `street_number` VARCHAR(5) NOT NULL ,  
  `street_name` VARCHAR(255) NOT NULL ,  
  `town_village` VARCHAR(50) NOT NULL ,  
  `county` VARCHAR(50) NOT NULL ,  
  `country` VARCHAR(45) NOT NULL ,  
  `postcode` VARCHAR(10) NOT NULL ,  
  PRIMARY KEY (`id`) ,  
  UNIQUE INDEX `street_number_UNIQUE` (`street_number` ASC, `street_name` ASC, `town_village` ASC,   `county` ASC, `country` ASC, `postcode` ASC) )  
ENGINE = InnoDB

**Image**  
CREATE  TABLE IF NOT EXISTS `warrington_main`.`image` (  
  `id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT ,  
  `alias_title` VARCHAR(255) NOT NULL ,  
  `title` VARCHAR(100) NOT NULL ,  
  `description` VARCHAR(2000) NOT NULL ,  
  `main_image` VARCHAR(50) NOT NULL ,  
  `thumbnail_image` VARCHAR(50) NOT NULL ,  
  `thumbnail_image_medium` VARCHAR(50) NOT NULL ,  
  `thumbnail_image_small` VARCHAR(50) NOT NULL ,  
  `thumbnail_image_gallery` VARCHAR(50) NOT NULL ,    
  `hits` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0' ,  
  `show_comment` ENUM('0','1') NOT NULL ,  
  `section` TINYINT(2) UNSIGNED NOT NULL ,  
  `flickr_youtube_id` VARCHAR(20) NOT NULL ,  
  `feature_in_gallery` ENUM('0','1') NOT NULL ,  
  `created_on` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' ,  
  `date_taken` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' ,  
  `updated_on` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' ,  
  `updated_by` MEDIUMINT(8) UNSIGNED NOT NULL ,  
  `approved` ENUM('Inprocess','Yes','No') NOT NULL DEFAULT 'Inprocess' ,  
  `visible` ENUM('0','1') NOT NULL DEFAULT '0' ,  
  PRIMARY KEY (`id`) ,  
  UNIQUE INDEX `alias_title` (`alias_title` ASC) ,  
  UNIQUE INDEX `flickr_youtube_id`   (`flickr_youtube_id` ASC) ,  
  INDEX `title` (`title` ASC) ,  
  INDEX `approved` (`approved` ASC) ,  
  INDEX `visible` (`visible` ASC) ,  
  INDEX `feature_in_gallery` (`feature_in_gallery` ASC) )  
ENGINE = InnoDB  
AUTO_INCREMENT = 23162  
DEFAULT CHARACTER SET = utf8  

现在每张图片都可以有地理位置和地址。我打算创建另一个名为location的表。

我打算为地理位置和地址创建具有可选关系的位置。换句话说,位置可以是地理位置或地址,也可以是两者。我不想存储null / null。第一个问题是如何在这个实例中创建一个具有两个可选关系的表,并确保我不会得到null / null。

然后我需要将位置表与图像表相关联。我可能希望将来针对另一个表(即事件)的位置或地址/或地理编码进行查询。

因此,事件的位置可能与存储在图像表中的位置相同。有人知道这是否是最好的结构。图像表/地址/地理位置,然后是一个位置和图像表。

换句话说,我有一个表,它包含两个与地理位置和地址的可选关系。我需要一个包含在表中但不是两个都是null。我该如何强制执行此约束

2 个答案:

答案 0 :(得分:0)

我看到你可以采用两种方式来构建它。任何一个图像都有外键来解决和地理定位。或者你可以让图像只有一个新的位置表的外键,而新的位置表又有外键关系来解决和地理位置。您想要做的可能取决于您是否希望将来有其他类型的位置类型。我个人会选择第一个,除非你想要放在一个单独的位置上的地理位置和地址的其他常见属性,或者你认为将来会有不同的位置类型。

在任何一种情况下,无论哪个字段具有地址和地理位置,其中的外键也可以在两个字段中具有唯一索引。这将(主要)强制执行所需的关系,因为您可以允许字段保存NULL值,然后您将在该表中最多有一个NULL-NULL记录。

如果您只是简单地对地址和地理位置表进行非规范化并添加列类型以指示它是哪种类型(地址,地理位置或两者),那么您可以严格执行关系,因为看起来这两个表的数据结构是相同。然后,您将使映像中此表的外键不为null,并且,如果使用innodb,则可以选择强制引用完整性。当然,这会限制你到一个实际地址(即不同的地址和地理定位地址)。

答案 1 :(得分:0)

如果你想确保位置表中的一行至少有一个位置字段不为空,我建议通过创建ON INSERT和相应的ON UPDATE触发器来确保这一点

诀窍是让触发器将NEW中的字段更改为不可插入的resp。不可能更新的值,因此INSERTUPDATE失败。一种可能性是强制重复键错误,另一种可能是强制无效值。像

这样的东西
CREATE TRIGGER needs_some BEFORE UPDATE ON location
FOR EACH ROW
BEGIN
     IF (NEW.Geolocation IS NULL AND NEW.Address IS NULL) THEN
         SET NEW.Geolocation = 'ABC';
     END IF;
END;

假设location.Geolocation是数字类型,这将使​​插入失败。

在MySQL 5.5或更高版本上你可以简单地

CREATE TRIGGER needs_some BEFORE UPDATE ON location
FOR EACH ROW
BEGIN
     IF (NEW.Geolocation IS NULL AND NEW.Address IS NULL) THEN
         SIGNAL SQLSTATE '99999' SET MESSAGE_TEXT='Must not be a double NULL';
     END IF;
END;

或早期的MySQL版本

CREATE TRIGGER needs_some BEFORE UPDATE ON location
FOR EACH ROW
BEGIN
     IF (NEW.Geolocation IS NULL AND NEW.Address IS NULL) THEN
         CALL inexistant_stored_procedure;
     END IF;
END;

作为解决方法。