0

我有三张桌子:

地址

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。

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

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

换句话说,我有一个表,其中包含两个与地理位置和地址的可选关系。我需要其中一个包含在表中,而不是两者都为空。我怎样才能执行这个约束

4

2 回答 2

0

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

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

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

于 2012-09-04T21:29:04.953 回答
0

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

诀窍是让触发器将一个字段更改NEW为一个不可能插入的字段。inmpossible-to-update 值,因此使INSERTorUPDATE失败。一种可能是强制重复键错误,另一种是强制无效值。就像是

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;

作为一种解决方法。

于 2012-09-04T21:32:51.953 回答