关于中间可选模型的基本数据库设计

时间:2010-07-09 06:06:51

标签: sql ruby-on-rails database database-design

我正在开展一个项目,在这个项目中我遇到一些看似非常简单的设计时遇到了一些困难:

user属于属city的{​​{1}},但country引用可能为citynull必须属于user country然而。换句话说(在基本 RoR模型语法中),

# class User < ActiveRecord::Base 
belongs_to :city
belongs_to :country
validates_existence_of :country

# class City < ActiveRecord::Base
has_many :users
belongs_to :country
validates_existence_of :country

# class Country < ActiveRecord::Base
has_many :users
has_many :cities    

我对这种超级简单设计的问题在于存在如此多的冗余。只要city引用了user,就可以从中推断country引用(换句话说,因为它已在city表中引用,在user表中引用它似乎并不那么令人敬畏。

3 个答案:

答案 0 :(得分:5)

当A(城市)也唯一地标识B(国家)时会发生这种情况,但A是可选的,而B是强制性的。基本上,国家/地区只是添加,因为城市是可选的,而仍然需要确定每个用户的国家/地区。

将国家和城市联系在一起的想法可能看起来很有吸引力,因为一个城市独特地“识别”了一个国家,但是:是吗?阿姆斯特丹不仅仅是荷兰的一个城市。

此外,它还包含您在评论中提到的问题...您如何处理其他数据;现在列出这些国家需要将它们从国家/城市合并中过滤掉。

您的原始设计可能感觉多余并且可能是数据方面,但逻辑方面和需求方面则不然。我会坚持,因为它非常清楚,完美地反映了要求。而且我会学会忍受令人难以置信的冗余。你可能想出的任何“解决方案”,以避免“冗余”,可能会最终混淆水域。或者将来更难定义查询。

答案 1 :(得分:1)

由于某种原因,这里有'sql'标签,所以这就是我在SQL中的做法(注意整个参考整合,没有NULL能列):

CREATE TABLE Countries 
(
 country_code CHAR(3) NOT NULL UNIQUE
);

CREATE TABLE Cities 
(
 city_name VARCHAR(20) NOT NULL, 
 country_code CHAR(3) NOT NULL 
    REFERENCES Countries (country_code), 
 UNIQUE (country_code, city_name)
);

CREATE TABLE Users
(
 username CHAR(8) NOT NULL UNIQUE, 
 country_code CHAR(3) NOT NULL, 
 UNIQUE (country_code, username)
);

CREATE TABLE UsersCountries
(
 username CHAR(8) NOT NULL UNIQUE, 
 country_code CHAR(3) NOT NULL, 
 FOREIGN KEY (country_code, username)
    REFERENCES Users (country_code, username), 
 city_name VARCHAR(20) NOT NULL, 
 FOREIGN KEY (country_code, city_name)
    REFERENCES Cities (country_code, city_name)
);

测试数据:

INSERT INTO Countries (country_code) VALUES 
('ITL'), 
('ESP');

INSERT INTO Cities (city_name, country_code) 
VALUES 
('Roma', 'ITL'), 
('Naples', 'ITL'), 
('Barcelona', 'ESP'), 
('Madrid', 'ESP');

INSERT INTO Users (username, country_code) VALUES 
('00000001', 'ESP'), 
('00000002', 'ESP'), 
('00000003', 'ITL'), 
('00000004', 'ITL');

INSERT INTO UsersCountries (username, city_name, country_code) 
VALUES 
('00000002', 'Madrid', 'ESP'), 
('00000004', 'Roma', 'ITL');

公平地说,大多数SQL编码人员都不会厌恶使用NULL能力列,而是希望所有用户的详细信息都出现在一个表中。假设您的SQL产品(正确)不将NULL视为值(例如,MS SQL Server不会将MS Access作为值),那么以下内容将起作用,并且等同于上述结构(即尽管参考完全依赖于整个NULL能列的存在:

CREATE TABLE Users
(
 username CHAR(8) NOT NULL UNIQUE, 
 city_name VARCHAR(20), 
 country_code CHAR(3) NOT NULL
    REFERENCES Countries (country_code), 
 FOREIGN KEY (country_code, city_name)
    REFERENCES Cities (country_code, city_name)
);

INSERT INTO Users (username, city_name, country_code) VALUES 
('00000001', NULL, 'ESP'), 
('00000002', 'Madrid', 'ESP'), 
('00000003', NULL, 'ITL'), 
('00000004', 'Roma', 'ITL');

答案 2 :(得分:0)

我没有深思熟虑的答案,但我首先想到的是,

# class User < ActiveRecord::Base 
belongs_to :country, :through => :city
validates_existence_of :city

# class City < ActiveRecord::Base
has_many :users
belongs_to :country
validates_existence_of :country

# class Country < ActiveRecord::Base
has_many :users, :through => :city
has_many :cities

诀窍是每个国家都会添加一个虚拟或空白城市,以便验证成立。