2009-10-30 345 views
0

設置:幫助MySQL查詢與許多加入

CREATE TABLE `contacts` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `last` varchar(100) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `first` varchar(100) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `prefix` varchar(50) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `suffix` varchar(50) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `address` varchar(100) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `address_1` varchar(100) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `city_id` int(100) DEFAULT NULL, 
    `state_id` int(20) DEFAULT NULL, 
    `alt_address_1` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `alt_address_2` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `alt_city` varchar(100) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `alt_state` varchar(20) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `alt_zip` varchar(15) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `publish_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `salutation` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `mail_label` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `solicitor` varchar(100) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `is_volunteer` tinyint(1) DEFAULT NULL, 
    `is_sponsor` tinyint(1) DEFAULT '0', 
    `is_company` tinyint(1) DEFAULT '0', 
    `is_foundation` tinyint(1) DEFAULT '0', 
    `status` varchar(15) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `created_on` datetime NOT NULL, 
    `created_by` varchar(30) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `modified_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    `modified_by` varchar(100) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `agency_id` int(25) DEFAULT NULL, 
    `primary_id` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `primary_id` (`primary_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=3008 DEFAULT CHARSET=utf8 

CREATE TABLE `cities` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `city` varchar(50) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `stateid` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `city` (`city`) 
) ENGINE=InnoDB AUTO_INCREMENT=128 DEFAULT CHARSET=utf8 

CREATE TABLE `states` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `abbreviation` varchar(2) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `state` varchar(20) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `state` (`state`), 
    UNIQUE KEY `abbreviation` (`abbreviation`), 
    KEY `id` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=52 DEFAULT CHARSET=utf8 

CREATE TABLE `zips` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `zip` varchar(10) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 
    `cityid` int(100) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `zip` (`zip`) 
) ENGINE=InnoDB AUTO_INCREMENT=128 DEFAULT CHARSET=utf8 
:使用4臺

  • 聯繫
  • 城市
  • 美國
  • 拉鍊

結構跟數據庫

我已經填寫了111個聯繫人的聯繫人,州只是所有的州,而城市有與州ID相關的相應的ID鍵,郵政編碼有一個鍵匹配一個城市。

該查詢將生成一個人員列表,以匹配適當的字段。這是查詢。

SELECT concat(contacts.last,' ', contacts.first) as name 
    , cities.city 
    , zips.zip 
    FROM contacts 
    JOIN cities 
    ON cities.id = contacts.city_id 
    JOIN states ON states.id = contacts.state_id 
    JOIN zips ON zips.cityid = cities.id 

該查詢返回可能的11個聯繫人的338行。有明顯的重複。當我加入郵政編碼時會發生這種情況,因爲它們屬於多個城市,因此每個城市都會匹配(我認爲這就是發生的情況)。任何人都有如何正確加入這些表的答案?

謝謝。 Rich

+1

我爲你清理你的代碼。在將來,請不要在一行中編寫SQL查詢。 – longneck 2009-10-30 17:43:01

+0

如果城市有多個拉鍊行,您希望發生什麼?你想要哪個郵編回來?回答這個問題,解決方案可能會遵循... – 2009-10-30 20:39:43

回答

1

我相信你應該重新考慮很多這些表上的代用鍵使用情況,並儘可能使用自然鍵。以狀態表爲例,在大多數情況下,簡單地將狀態短(即TX與Texas)用於數據和顯示目的是可以接受的。這意味着如果您刪除了狀態表上的遞增ID併爲每個狀態使用了一個自然鍵,則在90%的情況下,您將減少連接的必要性。

然後在需要存儲狀態值的表中使用state.abbriviation作爲FK。將此擴展到郵政編碼和城市,您可以將州縮寫到城市表格中,並將城市表格中的複合FK形成聯繫人表格,從而爲城市和州的關鍵字同時提供密鑰。

示例模式(不包括拉鍊碼錶,並縮短了接觸表):

CREATE TABLE IF NOT EXISTS `states` (
    `state_id` CHAR(2) NOT NULL , 
    `name` VARCHAR(45) NULL , 
    PRIMARY KEY (`state_id`) , 
    UNIQUE INDEX `state_name` (`name` ASC) 
) 
ENGINE = InnoDB; 

CREATE TABLE IF NOT EXISTS `cities` (
    `state_id` CHAR(2) NOT NULL , 
    `city_name` VARCHAR(255) NOT NULL , 
    PRIMARY KEY (`state_id`, `city_name`) , 
    INDEX `fk_city_state_id` (`state_id` ASC) , 
    CONSTRAINT `fk_city_state_id` 
    FOREIGN KEY (`state_id`) 
    REFERENCES `states` (`state_id`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION 
) 
ENGINE = InnoDB; 

CREATE TABLE IF NOT EXISTS `contacts` (
    `contacts_id` INT NOT NULL AUTO_INCREMENT , 
    `state` CHAR(2) NULL , 
    `city` VARCHAR(255) NULL , 
    PRIMARY KEY (`contacts_id`) , 
    INDEX `fk_contact_city` (`state` ASC, `city` ASC) , 
    INDEX `fk_contact_state` (`state` ASC) , 
    CONSTRAINT `fk_contact_city` 
    FOREIGN KEY (`state` , `city`) 
    REFERENCES `cities` (`state_id` , `city_name`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION, 
    CONSTRAINT `fk_contact_state` 
    FOREIGN KEY (`state`) 
    REFERENCES `states` (`state_id`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION 
) 
ENGINE = InnoDB; 

-- ----------------------------------------------------- 
-- Data for table `states` 
-- ----------------------------------------------------- 
SET AUTOCOMMIT=0; 
INSERT INTO `states` (`state_id`, `name`) VALUES ('TX', 'Texas'); 
INSERT INTO `states` (`state_id`, `name`) VALUES ('CA', 'California'); 
INSERT INTO `states` (`state_id`, `name`) VALUES ('OR', 'Oregon'); 
COMMIT; 

-- ----------------------------------------------------- 
-- Data for table `cities` 
-- ----------------------------------------------------- 
SET AUTOCOMMIT=0; 
INSERT INTO `cities` (`state_id`, `city_name`) VALUES ('CA', 'modesto'); 
INSERT INTO `cities` (`state_id`, `city_name`) VALUES ('OR', 'protland'); 
INSERT INTO `cities` (`state_id`, `city_name`) VALUES ('TX', 'Dallas'); 
COMMIT; 

現在查詢被簡化,除了在極端的情況下你需要一個完整的狀態命名:

SELECT 
    concat(contacts.last,' ', contacts.first) as name, 
    city, 
    state, 
    zip 
FROM contacts 
WHERE {INSERTWHERE} 
+0

有趣 - 我沒有意識到外鍵在mysql中被支持 - 我一直認爲它們沒有。我會嘗試這個解決方案,看看它是如何工作的。 謝謝 豐富 – 2009-10-30 23:58:16

+0

他們支持,只要你使用INNODB :) – 2009-10-31 00:27:31

+0

我正在制定這個例子,但有幾個問題。 您是否會在city_name - > zipcode上創建與拉鍊類似的關係? 此外,在聯繫人表 - 查找完整的州名,我會繼續使用聯接。正確? 感謝您對此的幫助。 – 2009-10-31 15:41:08

0

您的表格正確加入。我認爲你在這裏遇到的問題是你的數據不正常並且太過分了。只需存儲提供的地址即可。不要試圖用數字ID將它分成表格。

例如,通過將狀態作爲數字存儲在聯繫人表中而不僅僅是狀態碼,您可以獲得什麼樣的好處?而同樣的問題適用於城市。

+0

州,城市和郵政編碼數據可用於各種場所。表聲音在這裏沒有幫助。然而,重新考慮代理鍵會。 – 2009-10-30 17:58:48