2012-09-04 20 views
0

我有三個表: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。

然後我需要將位置表關聯到圖像表。我可能希望將來針對另一個表的位置或地址/或地理編碼進行查詢,即事件。

所以事件的位置可能與圖像表中存儲的位置相同。有人知道這是否是最好的結構。圖像表格/地址/地理位置,然後是圖像表格。

換句話說,我有一個表格,它包含兩個可選的地理位置和地址關係。我會需要任何一個包括在表中,但不是兩個都是空的。我如何執行這個約束

回答

0

我看到了你可以構造這個的兩種方式。任何一個圖像都可以擁有外鍵來解決地理定位問題。或者你可以讓圖像只有一個新的位置表的外鍵,而這個新的位置表又具有外鍵關係來解決和地理定位。你想要做什麼可能取決於你是否期望將來有其他種類的位置類型。我會親自選擇第一個,除非您希望將其他地理位置和地址的其他共同屬性放置在不同的位置,或者您認爲將來如上所述將採用不同的位置類型。

在任何一種情況下,無論哪個字段具有地址和地理位置外鍵都可以在兩個字段之間具有唯一的索引。這將(大部分)強制執行所需的關係,因爲您可以允許字段保存NULL值,然後在該表中最多隻有一個NULL-NULL記錄。

要麼你或者可以嚴格執行關係,如果你簡單地對地址和地理位置表進行非規範化並添加一個列類型來表明它是哪種類型(地址,地理位置或者兩者),因爲它看起來兩個表的數據結構是一樣的。然後,您將使映像中該表的外鍵不爲空,並且如果使用innodb,則可以選擇強制執行參照完整性。當然這會限制你一個實際的地址(即沒有不同的地址和地理位置地址)。

+0

我想,以選擇第二個選項爲我在其他表在未來的搜索。即一個事件的位置可能與圖像相同。然後,我可以使用相同的位置/地址或地理編碼查找事件和圖像。如果我在圖像中創建了一個關於地理編碼的關係,並且解決了這個問題,將很難看出哪個事件具有相同的地理編碼和地址。我確實需要確保參照完整性,但是一個位置可以有可選地址或可選地理位置。我們可以有一個位置或地址的座標。 –

+0

在這種情況下是最好能有參考intergrity與將指定位置的類型另一個領域。或增加一個檢查與觸發器 –

+0

@MatthewChambers那麼它聽起來就像你已經回答了你自己的問題與問候模式。有一個單獨的位置表在你的情況下是有道理的。參照完整性的問題在於,它會迫使你爲每個地址和地理位置外鍵字段賦值。所以,你的情況,你想允許有字段或兩個字段不能夠被執行,因爲你希望能夠有在這些領域NULL值。 –

0

如果你想確保,在位置表竟位置字段中的至少一個排不爲空,我建議通過創建一個ON INSERT和相應ON UPDATE觸發,以確保這一點。

訣竅是讓觸發器將NEW中的一個字段更改爲不可插入或不可插入的字段。不可能更新的值,因此使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; 

作爲一種解決方法。