数据库设计/建模问题 - 约束还是没有约束?

时间:2009-07-20 18:16:22

标签: database-design normalizing

鉴于以下结构:

City
Area
User

每个区域都有1个且只有1个城市 每个用户至少有一个但可能有多个区域 每个用户只有1个城市。

对此进行建模的最优雅方式是什么?

目前,我有:

User,
UserArea,
Area,
City

其中UserArea与用户的1:M关系,与City的区域为1:1。

问题在于:

用户在当前模型下可以有3个或4个区域,但其中2个区域可以在城市“1”中,其他2个区域可以在城市“2”中。这违反了业务规则。

我应该只是设置一个约束来防止这种事情,还是一种更好的方法来进一步规范化,以便这种类型的悖论是不可能的?如果是这样,那么如何对该系统进行建模:

1个用户= 1个城市;
1区= 1城市;
1个用户= M个区域;

感谢您的见解。

7 个答案:

答案 0 :(得分:1)

我将为用户,区域和城市分别提供一个表格,然后有第四个表格,其中包括列用户(FK),城市(FK)和区域(FK),其中Users&城市(组合)被限制为独特。然后,只要插入用户区组合,就不允许使用非唯一城市。

答案 1 :(得分:0)

我唯一能想到的就是:

为Area表提供CityID和AreaID的复合备用键。使AreaID成为主要的(因此它只能有一个城市)。

使用此备用键(AK1)在Area和UserArea之间形成FK关系。

为User表提供UserID和CityID的复合备用键。将UserID设为主要。

使用此备用密钥(AK2)在User和UserArea之间形成FK关系。

所以你的UserArea表将如下所示:

用户名 CityID areaID表示

基于AK2的外键将强制您选择与用户所在城市相匹配的城市,而基于AK1的外键将强制您选择属于​​该城市的区域。实质上,AK1和AK2外键会重叠,强制你想要的东西。

答案 2 :(得分:0)

我认为您的“用户,用户区域,区域,城市”方法是正确的。依靠约束和业务逻辑来防止违规。

答案 3 :(得分:0)

您能否提供有关区域的更多详细信息?让我说出我的假设:
用户住在城市。
每个城市都有区域 一个区域只能落入一个城市 用户只能住在一个城市 鉴于这些条件,您似乎在设计规范中具有以下功能依赖性:
面积 - >市
用户 - >市
您的商业模式表明,用户可以在同一城市内拥有多个地址,但不能在两个不同的城市拥有地址。这是一个现实的设计约束吗?如果我可以有多个地址,为什么不在不同的城市呢?
如果要存储给定用户的所有区域,则需要第三个表(如您所建议的那样)。该表看起来像是UserArea(userID,AreaID)。您需要使用触发器或存储过程来实现业务逻辑。

答案 4 :(得分:0)

USER_AREAS只需要以下列:

  • USER_ID(pk,fk代表USERS.USER_ID
  • AREA_ID(pk,fk代表AREA.AREA_ID

区域与AREAS表中的一个城市相关联;您可以通过从AREAS表中汇总来了解哪些城市与用户相关联:

AREA

  • AREA_ID(pk)
  • CITY-ID(fk代表CITY.CITY_ID

CITY_ID放在USER_AREAS表中是多余的。其次,将CITY_ID放在USER_AREAS表中并不能保证该记录中的AREA_ID实际上与AREA表中的CITY_ID相关联。 CHECK约束仅通过限制列接受的值来强制域完整性,并且不能引用其他表中的列必须少于用户定义的函数。

您无法强制执行仅属于数据库中单个城市的用户区域的业务规则。它必须在应用程序级别完成(无论sproc管理插入/更新USER_AREAS表)。

答案 5 :(得分:0)

我不确定“区域”是什么意思。

我认为城市区划如下:

这个星球有国家。 一个国家有地区(州,省等) 地区有地区(城市,城镇,村庄等) 区域(如果足够大)可以有区域。

用户=>国家+地区/地区+城市(+地区)

请您详细说明一下?

答案 6 :(得分:0)

这个答案是从SQLServerCentral提供给我的,它完全符合我的要求。存在冗余(正如本论坛中指出的那样),但不存在异常现象。

我对你的意见和建议很感兴趣。


CREATE TABLE [dbo].[Cities](
    [CityID] [int] IDENTITY(1,1) NOT NULL,
  [CityName] [varchar](50) NOT NULL,
 CONSTRAINT [PK_Cities] PRIMARY KEY CLUSTERED
(
   [CityID] ASC
)
)
CREATE TABLE [dbo].[Users](
   [UserID] [int] IDENTITY(1,1) NOT NULL,
  [UserName] [varchar](50) NOT NULL,
  [CityID] [int] NOT NULL,
 CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
  [UserID] ASC
)
)

ALTER TABLE [dbo].[Users]  WITH CHECK ADD  CONSTRAINT [FK_Users_Cities] FOREIGN KEY([CityID])
REFERENCES [dbo].[Cities] ([CityID])
GO
ALTER TABLE [dbo].[Users] CHECK CONSTRAINT [FK_Users_Cities]
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_UsersCity] ON [dbo].[Users]
(
   [UserID] ASC,
   [CityID] ASC
)

CREATE TABLE [dbo].[Areas](
    [AreaID] [int] IDENTITY(1,1) NOT NULL,
  [AreaName] [varchar](50) NOT NULL,
  [CityID] [int] NOT NULL,
 CONSTRAINT [PK_Areas] PRIMARY KEY CLUSTERED
(
  [AreaID] ASC
))


GO
ALTER TABLE [dbo].[Areas]  WITH CHECK ADD  CONSTRAINT [FK_Areas_Cities] FOREIGN KEY([CityID])
REFERENCES [dbo].[Cities] ([CityID])
GO
ALTER TABLE [dbo].[Areas] CHECK CONSTRAINT [FK_Areas_Cities]
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_AreasCity] ON [dbo].[Areas]
(
 [AreaID] ASC,
   [CityID] ASC
)
GO
CREATE TABLE [dbo].[UserCityArea](
   [UserID] [int] NOT NULL,
    [CityID] [int] NOT NULL,
    [AreaID] [int] NOT NULL,
 CONSTRAINT [PK_UserCityArea] PRIMARY KEY CLUSTERED
(
   [UserID] ASC,
   [CityID] ASC,
   [AreaID] ASC
)
)

GO
ALTER TABLE [dbo].[UserCityArea]  WITH CHECK ADD FOREIGN KEY([UserID], [CityID])
REFERENCES [dbo].[Users] ([UserID], [CityID])
GO
ALTER TABLE [dbo].[UserCityArea]  WITH CHECK ADD FOREIGN KEY([AreaID], [CityID])
REFERENCES [dbo].[Areas] ([AreaID], [CityID])