数据库规范化混乱?

时间:2016-02-26 11:02:31

标签: sql normalization

我有5张桌子:

  1. tblProvince
  2. tblDivison
  3. tblDistrict
  4. tblCity
  5. tblconstituency
  6. tblProvince

    • ID
    • 姓名

    TblDivision

    • ID
    • 名称
    • Province_id

    tblDistrict

    • ID
    • 名称
    • Province_id
    • Division_id

    tblCity

    • ID
    • 名称
    • Province_id
    • Division_id
    • District_id

    TblConstituency

    • ID
    • 名称
    • Province_id
    • Division_id
    • District_id
    • City_id

    这是存储数据的正确方法吗?添加区域的完整细节。

    或者我应该保存前一个区域的id(就像城市是选区的容器)。存储选区的详细信息我应该使用city_id。

    像这样......

    tblCity
    +-------------+
    | ID          |
    +-------------+
    | Name        |
    +-------------+
    | Area        |
    +-------------+
    | District_id |
    +-------------+
    
    TblConstituency
    +-------------+
    | ID          |
    +-------------+
    | Name        |
    +-------------+
    | Area        |
    +-------------+
    | City_id     |
    +-------------+
    

    如果我想得到关于选区的完整细节我可以得到城市的id,并且从城市我可以得到地区,并且从地区我可以得到分裂等等。

    但是,我觉得这是一个巨大的矫枉过正。每当我需要获取细节时,这将是一个很长的查询。

    那么,最好的方法是什么?

    P.S:对于描述我的问题的方式很糟糕道歉。

3 个答案:

答案 0 :(得分:2)

永远不要存储两次相同的信息。这意味着您必须手动保持信息同步,并且同步很困难且容易出错。基本上,只要你有多个真理来源,你就没有真相来源。

在这里考虑你的表:

Division
+-------------+
| ID          |
+-------------+
| Name        |
+-------------+
| Area        |
+-------------+
| Province_id |
+-------------+



District
+-------------+
| ID          |
+-------------+
| Name        |
+-------------+
| Area        |
+-------------+
| Province_id |
+-------------+
| Division_id |
+-------------+

Division已存储Province_id。那么为什么District也需要存储呢?如果District存储不同的 Province_id而不是相应的Division记录,会发生什么情况?哪一个对District是正确的?

只需链接到直接父记录:

District
+-------------+
| ID          |
+-------------+
| Name        |
+-------------+
| Area        |
+-------------+
| Division_id |
+-------------+

Division表相关的信息已经存在且可以查询。 (基本上,这就是JOIN关键字的用途。)由于您已经掌握了这些信息,因此无需重复。

答案 1 :(得分:1)

通过BCNF进行规范化是基于功能依赖性。什么 是这样的数据中的功能依赖?什么是 候选人钥匙?

Cities
State       County       City
--
Alabama     Pike         Troy
Arkansas    Pike         Delight
Florida     Bay          Springfield
Maine       Penobscot    Springfield

这里只有一个(普通的)功能依赖,只有一个 候选人的关键唯一的FD是State,County,City - >州/县, 市。唯一的候选关键是{州,县,市}。这种关系 是至少5NF。

无法改善这种关系,但你可以改进 数据库。数据库不知道没有名为“Los 安吉利斯“在阿拉巴马州。所以它会让你插入这个无效的行。

Cities
State       County       City
--
Alabama     Los Angeles  Troy

要修复问题,请添加包含所有有效内容的关系 县,并设置外键引用。

Counties
State
--
Alabama     Autauga
Alabama     Baldwin
...
Alabama     Pike
...
California  Los Angeles
...

关系“Counties”都是关键,它没有非素数 属性。 “县”也至少在5NF。

数据库仍然不知道它不应该允许这样的行。

Cities
State    County  City
--
Wales    Pike    Troy

在美国没有名为 Wales 的州。解决这个问题一样 作为最后一个问题。

States
--
Alabama
Arkansas
...
California
...

并设置从郡到州的外键引用。

这是标准SQL中的样子,除了我没有 供应所有50个州或所有3000多个县。

create table states (
  state varchar(100) primary key
);

insert into states values 
('Alabama'), ('Arkansas'), ('California'), ('Florida'), 
('Maine'); -- and more . . .

create table counties (
  county varchar(100) not null,
  state varchar(100) not null,
  primary key (county, state),
  foreign key (state) references states (state)
    on update restrict on delete restrict
);

insert into counties values
('Autauga', 'Alabama'), ('Baldwin', 'Alabama'), ('Pike', 'Alabama'), 
('Pike', 'Arkansas'), 
('Los Angeles', 'California'),
('Bay', 'Florida'), 
('Penobscot', 'Maine'); -- and more . . .

create table cities (
  city varchar(100) not null,
  county varchar(100) not null,
  state varchar(100) not null,
  primary key (city, county, state),
  foreign key (county, state) references counties (county, state)
    on update restrict on delete restrict
);

insert into cities values 
('Troy', 'Pike', 'Alabama'),
('Delight', 'Pike', 'Arkansas'),
('Springfield', 'Penobscot', 'Maine'),
('Springfield', 'Bay', 'Florida'); -- and more . . .

现在你会发现插入无效元组是不可能的 {特洛伊,洛杉矶,阿拉巴马州}和{特洛伊,派克,威尔士}。

使用代理ID号而不是自然键不会改变 正常形式。但它确实改变了数据库的工作方式。并不是 必须以一种好的方式。

使用上面的SQL表,此更新将失败。

update states
set state = 'Wibble'
where state = 'Alabama';

这是一件好事。

让我们用代理ID号来构建这些表。

create table states (
  state_id integer primary key,
  state varchar(100) not null unique
);

insert into states values 
(1, 'Alabama'), (2, 'Arkansas'), (3, 'California'), (4, 'Florida'), 
(5, 'Maine'); -- and more . . .

create table counties (
  county_id integer not null,
  county varchar(100) not null,
  state_id integer not null, 
  foreign key (state_id) references states (state_id) 
    on update restrict on delete restrict,
  primary key (county_id, state_id),
  unique (county, state_id)
);

insert into counties values
(1, 'Autauga', 1), (2, 'Baldwin', 1), (3, 'Pike', 1), 
(4, 'Pike', 2), 
(5, 'Los Angeles', 3),
(6, 'Bay', 4), 
(7, 'Penobscot', 5); -- and more . . .


create table cities (
  city_id integer not null,
  city varchar(100) not null,
  county_id integer not null,
  state_id integer not null,
  foreign key (county_id, state_id) references counties (county_id, state_id)
    on update restrict on delete restrict,
  primary key (city_id, county_id, state_id),
  unique (city, county_id, state_id)
);

insert into cities values 
(1, 'Troy', 3, 1),
(2, 'Delight', 4, 2),
(3, 'Springfield', 7, 5),
(4, 'Springfield', 6, 4); -- and more . . .

这三张表中的所有三张表仍然至少为5NF。但是这个 (无效)更新现在将成功。

update states
set state = 'Wibble'
where state = 'Alabama';

这是一件坏事。

使用代理ID号进行每个外键引用 具有与声明它们on update cascade相同的行为。恢复 on update restrict的语义的一部分,你必须采取 额外的,非直观的撤销更新权限的步骤 引用表。

几乎 nobody 正确地获得该部分。

没有任何实际原则可以证明拆分主键是正确的 为了获得可以遵循的路径来恢复原始路径 关系。换句话说,没有关系原则 有理由改变这个......

Cities
city_id  city  county_id state_id
--
1        Troy  3         2

......对此。

Cities
city_id  city   county_id 
--
1        Troy   3
Counties
county_id county  state_id
--
3         Pike    1

不仅没有关系原则证明了分裂的合理性 主键,它创建关系模型的问题之一 旨在解决的数据。查找“IMS”,一个分层数据库 管理系统,要求用户遵循路径 数据文件。

答案 2 :(得分:0)

只是为了澄清另一个答案并删除冗余,这可能会通过各自的查询/连接来澄清。我正在重命名列以确保上下文的澄清,但可能缺少“区域”领域的背景

tblProvince:     ( ID, ProvinceName, Area )
TblDivision:     ( ID, DivisionName, ProvinceID )
tblDistrict:     ( ID, DistrictName, DivisionID )
tblCity:         ( ID, CityName, DistrictID )
TblConstituency: ( ID, ConstituencyName, CityID )

select
      Con.ConstituencyName, 
      City.CityName,
      Dis.DistrictName,
      Div.DivisionName,
      Prov.ProvinceName,
      Prov.Area
   from
      tblConstituency Con
         JOIN tblCity City
            on Con.CityID = City.ID
            JOIN tblDistrict Dis
               on City.DistrictID = Dis.ID
               JOIN tblDivision Div
                  on Dis.DivisionID = Div.ID
                  JOIN tblProvince Prov
                     on Div.ProvinceID = Prov.ID

并注意JOINS从一个表到下一个表的层次结构表示。您可以看到表的直接相关性。如果您正在寻找有问题的特定区域,那么只需应用WHERE子句。