Помощь с MySQL Query со многими объединениями - PullRequest
0 голосов
/ 30 октября 2009

Настройка: база данных контактов с использованием 4 таблиц

  • Контакты
  • Город
  • States
  • Молнии

Состав:

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

Я заполнил контакты 111 контактами, состояния - это просто все штаты, и города имеют соответствующие идентификационные ключи, которые относятся к идентификатору штата, почтовые индексы имеют ключ, соответствующий городу.

Запрос состоит в том, чтобы создать список людей, которые соответствуют соответствующим полям. Вот запрос.

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

Этот запрос возвращает 338 строк из 11 возможных контактов. Там очевидные дубликаты. Это происходит, когда я присоединяюсь к почтовым индексам, которые, поскольку они принадлежат более чем одному городу, сопоставляются для каждого города (я думаю, это то, что происходит). У кого-нибудь есть ответ, как правильно объединить эти таблицы?

Спасибо. Рич

Ответы [ 2 ]

1 голос
/ 30 октября 2009

Я считаю, что вам следует переосмыслить использование суррогатного ключа во многих из этих таблиц и по возможности использовать естественные ключи. Взяв в качестве примера таблицу состояний, в большинстве случаев будет допустимо просто использовать краткость состояния (т. Е. TX по сравнению с Техасом) для данных и отображения. Это означает, что если вы удалили инкрементный идентификатор в таблице состояний и использовали естественный ключ для каждого состояния, вы снизили бы необходимость объединения в 90% случаев.

Затем используйте state.abbriviation в качестве FK в таблицах, в которых необходимо хранить значения состояний. Расширяя это на почтовые индексы и города, вы можете FK штата abbr в таблицу городов и сделать составной 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 голосов
/ 30 октября 2009

ваши столы правильно соединены. Я думаю, проблема в том, что вы неправильно нормализовали свои данные и зашли слишком далеко. просто сохраните адрес, как указано. не пытайтесь разбить его на таблицы с числовыми идентификаторами.

Например, какую возможную выгоду вы получите, сохранив состояние в виде числа в таблице контактов, а не просто код состояния? и тот же вопрос относится к городу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...