规范化使得跨多个表的连接变得困难

时间:2011-11-22 19:45:33

标签: mysql join normalization

我有一个包含商店名称和地址的商店表。经过一番讨论后,我们现在正在对表进行规范化,将地址放在单独的表中。这样做有两个原因:

  1. 按地点/地址提高商店的搜索速度
  2. 在导入商店时,使用Levenshtein algorithm增加检查拼写错误的街道名称的执行时间。
  3. 新结构如下所示(忽略拼写错误):

    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. 考虑到上述原因,这是不正常的标准化吗?
    4. 更新

      以下代码似乎列出了所有具有正确地址的商店 - 但它有点慢(我有大约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 个答案:

答案 0 :(得分:1)

我不会跟拼写错误说话。由于您要导入数据,因此可以在临时表中更好地处理拼写错误。

让我们看看这个略微简化的版本。

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');

虽然很多人坚信邮政编码决定了美国的城市和州,但事实并非如此。邮政编码与运营商驾驶路线的方式有关,而与地理位置无关。一些城市跨越国家之间的边界;单个邮政编码路由可以跨越州行。即使是Wikipedia knows this,尽管他们的例子可能已经过时了。 (送货路线不断变化。)

所以我们有一个有两个候选键的表,

  • {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.

您可以(也可能应该)对城市名称,州名(州代码)和国家/地区名称重复此过程。

您的方法存在一些问题

您可以显示输入美国街道的街道ID号码以及克罗地亚的国家/地区ID。 (可以说,一个城市的“全名”是你可能想要存储的事实,以提高数据的完整性。对于街道的“全名”来说,这也许也是如此。)

对每个数据位使用id号会大大增加所需的连接数。使用id号与规范化无关。使用自然键上没有相应唯一约束的id号 - 一个完全普通的错误 - 允许重复数据。

相关问题