Sql 中间可选模型的基础数据库设计
我正在从事一个项目,在这个项目中,我在设计一个看似非常简单的场景时遇到了一些小困难:Sql 中间可选模型的基础数据库设计,sql,ruby-on-rails,database,database-design,Sql,Ruby On Rails,Database,Database Design,我正在从事一个项目,在这个项目中,我在设计一个看似非常简单的场景时遇到了一些小困难: 用户属于属于国家的城市,但是,城市引用可能为空,而用户必须属于国家。换句话说(在基本RoR模型语法中) #类用户
用户
属于属于国家
的城市
,但是,城市
引用可能为空
,而用户
必须属于国家
。换句话说(在基本RoR模型语法中)
#类用户
我对这种超级简单设计的问题是,有太多的冗余。只要一个
城市
被用户
引用,就可以从中推断出国家
引用(换句话说,因为它已经在城市
表中被引用了,所以在用户
表中也引用它似乎不是那么可怕)。我没有深思熟虑的答案,但我首先想到的是
# 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
#类用户:城市
验证是否存在:城市
#class City:城市
有很多城市吗
诀窍是在每个国家/地区添加一个虚拟或空白城市,以便验证有效。当a(城市)也唯一标识B(国家)时,会发生这种情况,但a是可选的,而B是强制性的。基本上,之所以添加Country,是因为City是可选的,而仍然需要标识每个用户的国家/地区 将国家和城市联系在一起的想法可能看起来很有吸引力,因为一个城市独特地“识别”了一个国家,但是:是吗?你知道,阿姆斯特丹不仅仅是荷兰的一个城市 另外,它带来了你在评论中已经提到的问题。。。您如何处理附加数据;而将这些国家列为此类国家现在需要将它们从国家/城市合并中过滤出来
您的原始设计可能会觉得冗余和数据方面的冗余,但逻辑方面和需求方面的冗余则不然。我会坚持它,因为它非常清楚,完美地反映了需求。我将学会忍受明显的冗余。你为避免“冗余”而提出的任何“解决方案”,最终都可能会把事情弄得一团糟。或者将使将来定义查询变得更加困难。出于某种原因,这有“sql”标记,因此我在sql中是这样做的(注意,整个sql中都有引用整数,没有
NULL
able列):
测试数据:
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
able列,而是更喜欢将所有用户的详细信息显示在一个表中。假设您的SQL产品(正确地)没有将NULL
视为一个值(例如,MS SQL Server没有,但MS Access有),则以下内容将起作用,并与上述结构等效(即,尽管存在NULL
可编辑列,但仍然贯穿引用整数):
+1在这一点上,有时正常化会造成比必要时更大的痛苦。我想补充的唯一一点是,您可以而且可能应该进行验证,以确保该城市位于选定的国家/地区。Marjan,非常感谢您的输入(我得到的越多,我对使用某个解决方案的感觉就越好)。你对学习这类东西有什么建议吗?我是应该选一本关于数据库设计的书,还是有好的网络资源?关于这个话题的问题是,我寻找这个问题的答案,但实际上只是空手而归。我担心我在数据库设计方面所做的所有决定都可能远远不够完美…@Geoff,对,谢谢你支持我的回答。还有+1提醒我确认这个城市(我显然是在计划它,但我很容易忘记这些事情是随着Rails的敏捷性发展而来的)。@tjko:很抱歉,我没有推荐书名之类的东西。总的来说:不要拘泥于数据库设计,要高高在上,想想你正在建模的东西。如果它们是不同的概念,请将它们分开。除非有(经证实的)需要,否则不要进行优化。除此之外:多与经验丰富的人交谈(像这里这样),了解他们的观点,即使他们相互矛盾,你也会对赞成者和反对者有一种感觉。多谢大家的意见,OmniBus!谢谢你的回复。我将其标记为SQL,因为我认为精通SQL的人可能会对数据库设计提供很好的见解,但我可能错误地认为,对不起(我是新来的)。无论如何,谢谢你的回答,我能读一点,但我必须说它的水平有点太低了,没有太多的设计问题,我要说的是。重点是,这些设计没有重复!当然,所有三个/四个表都会显示国家代码,但每个表都是维护数据完整性所必需的。你不能从一个城市中“推断”一个国家(例如,它是巴黎、德克萨斯还是巴黎、法国?),因此使用复合键。你确定一个城市一定在一个国家吗?反例可能是柏林(过去糟糕的日子)和耶路撒冷(现在)。我想还有其他的。很好,我不知道我该如何处理这些异常。。。
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');
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');