Нормализация затрудняет объединение нескольких таблиц - PullRequest
1 голос
/ 22 ноября 2011

У меня была таблица магазинов, содержащая название и адрес магазина. После некоторого обсуждения мы теперь нормализуем таблицу, поместив адрес в отдельные таблицы. Это сделано по двум причинам:

  1. Увеличение скорости поиска магазинов по местоположению / адресу
  2. Увеличение времени выполнения проверки названий улиц с ошибками с использованием алгоритма Левенштейна при импорте магазинов.

Новая структура выглядит следующим образом (игнорируйте опечатки):

country;
+--------------------+--------------+------+-----+---------+-------+  
| Field              | Type         | Null | Key | Default | Extra |  
+--------------------+--------------+------+-----+---------+-------+  
| id                 | varchar(2)   | NO   | PRI | NULL    |       |  
| name               | varchar(45)  | NO   |     | NULL    |       |  
| prefix             | varchar(5)   | NO   |     | NULL    |       |  
+--------------------+--------------+------+-----+---------+-------+  

city;
+--------------------+--------------+------+-----+---------+-------+  
| Field              | Type         | Null | Key | Default | Extra |  
+--------------------+--------------+------+-----+---------+-------+  
| id                 | int(11)      | NO   | PRI | NULL    |       |  
| city               | varchar(50)  | NO   |     | NULL    |       |  
+--------------------+--------------+------+-----+---------+-------+  

street;
+--------------------+--------------+------+-----+---------+-------+  
| Field              | Type         | Null | Key | Default | Extra |  
+--------------------+--------------+------+-----+---------+-------+  
| id                 | int(11)      | NO   | PRI | NULL    |       |  
| street             | varchar(50)  | YES  |     | NULL    |       |  
| fk_cityID          | int(11)      | NO   |     | NULL    |       |  
+--------------------+--------------+------+-----+---------+-------+  

address;
+--------------------+--------------+------+-----+---------+-------+  
| Field              | Type         | Null | Key | Default | Extra |  
+--------------------+--------------+------+-----+---------+-------+  
| id                 | int(11)      | NO   | PRI | NULL    |       |  
| streetNum          | varchar(10)  | NO   |     | NULL    |       |  
| street2            | varchar(50)  | NO   |     | NULL    |       |  
| zipcode            | varchar(10)  | NO   |     | NULL    |       |  
| fk_streetID        | int(11)      | NO   |     | NULL    |       |  
| fk_countryID       | int(11)      | NO   |     | NULL    |       |  
+--------------------+--------------+------+-----+---------+-------+  
*street2 is for secondary reference or secondary address in e.g. the US.

store;
+--------------------+--------------+------+-----+---------+-------+  
| Field              | Type         | Null | Key | Default | Extra |  
+--------------------+--------------+------+-----+---------+-------+  
| id                 | int(11)      | NO   | PRI | NULL    |       |  
| name               | varchar(50)  | YES  |     | NULL    |       |
| street             | varchar(50)  | YES  |     | NULL    |       |    
| fk_addressID       | int(11)      | NO   |     | NULL    |       |  
+--------------------+--------------+------+-----+---------+-------+  
*I've left out address columns in this table to shorten code

Новые таблицы заполнены правильными данными, и остается только добавить внешний ключ address.id в таблицу store.

Следующий код правильно перечисляет все названия улиц:

select a.id, b.street, a.street2, a.zipcode, c.city, a.fk_countryID
from address a
left join street b on a.fk_streetID = b.id
left join city c on b.fk_cityID = c.id
  1. Как мне обновить fk_addressID в store таблице?
  2. Как я могу перечислить все магазины с правильным адресом?
  3. Это плохая нормализация с учетом приведенных выше причин?

UPDATE

Кажется, что следующий код перечисляет все магазины с правильным адресом - однако это немного медленно (у меня около 2000 магазинов):

select a.id, a.name, b.id, c.street
from sl_store a, sl_address b, sl_street c
where b.fk_streetID = c.id
and a.street1 = c.street
group by a.name
order by a.id

1 Ответ

1 голос
/ 07 декабря 2011

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

Давайте посмотрим на эту слегка упрощенную версию.

create table stores
(
  store_name varchar(50) primary key,
  street_num varchar(10) not null,
  street_name varchar(50) not null,
  city varchar(50) not null,
  state_code char(2) not null,
  zip_code char(5) not null,
  iso_country_code char(2) not null,
  -- Depending on what kind of store you're talking about, you *could* have
  -- two of them at the same address. If so, drop this constraint.
  unique (street_num, street_name, city, state_code, zip_code, iso_country_code)
);  

insert into stores values 
('Dairy Queen #212',  '232', 'N 1st St SE',   'Castroville',  'CA', '95012', 'US'),
('Dairy Queen #213',  '177', 'Broadway Ave',  'Hartsdale',    'NY', '10530', 'US'),
('Dairy Queen #214', '7640', 'Vermillion St', 'Seneca Falls', 'NY', '13148', 'US'),
('Dairy Queen #215', '1014', 'Handy Rd',      'Olive Hill',   'KY', '41164', 'US'),
('Dairy Mart #101',   '145', 'N 1st St SE',   'Castroville',  'CA', '95012', 'US'),
('Dairy Mart #121',  '1042', 'Handy Rd',      'Olive Hill',   'KY', '41164', 'US');

Хотя многие люди твердо уверены, что почтовый индекс определяет город и штат в США, это не так. Почтовые индексы связаны с тем, как курьеры управляют своими маршрутами, а не с географией. Некоторые города пересекают границы между штатами; Маршруты с одним почтовым индексом могут пересекать государственные границы. Даже Википедия знает это , хотя их примеры могут быть устаревшими. (Маршруты доставки меняются постоянно.)

Итак, у нас есть таблица с двумя ключами-кандидатами,

  • {store_name} и
  • {street_num, street_name, city, state_code, zip_code, iso_country_code}

У него нет неключевых атрибутов. Я думаю, что эта таблица в 5NF. Что ты думаешь?

Если бы я хотел повысить целостность данных для названий улиц, я мог бы начать с чего-то вроде этого.

create table street_names
(
  street_name varchar(50) not null,
  city varchar(50) not null,
  state_code char(2) not null,
  iso_country_code char(2) not null,
  primary key (street_name, city, state_code, iso_country_code)
);  

insert into street_names
select distinct street_name, city, state_code, iso_country_code
from stores;

alter table stores
add constraint streets_from_street_names
foreign key             (street_name, city, state_code, iso_country_code)
references street_names (street_name, city, state_code, iso_country_code);
-- I don't cascade updates or deletes, because in my experience
-- with addresses, that's almost never the right thing to do when a 
-- street name changes.

Вы можете (и, вероятно, должны) повторить этот процесс для названий городов, названий штатов (кодов штатов) и названий стран.

Некоторые проблемы с вашим подходом

Вы, очевидно, можете ввести номер идентификатора улицы для улицы, которая находится в США, вместе с идентификатором страны для Хорватии. («Полное название» города, так сказать, это тот факт, который вы, вероятно, хотите сохранить, чтобы повысить целостность данных. Это, вероятно, также относится к «полному названию» улицы.)

Использование идентификационных номеров для каждого бита данных значительно увеличивает количество требуемых объединений. Использование идентификационных номеров не имеет ничего общего с нормализацией. Использование идентификационных номеров без соответствующих уникальных ограничений на естественные ключи - совершенно распространенная ошибка - позволяет дублировать данные.

...