我正在开展一个项目,在这个项目中我遇到一些看似非常简单的设计时遇到了一些困难:
user
属于属city
的{{1}},但country
引用可能为city
而null
必须属于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
表中引用它似乎并不那么令人敬畏。
答案 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
诀窍是每个国家都会添加一个虚拟或空白城市,以便验证成立。