Unc*_*cle 5 sql-server-2005 database-design constraint
我正在 Microsoft SQL Server 2005 上开发企业培训预订系统的第二个版本。
我有 3 个表(针对这个问题进行了简化)。
表 1 - CourseSize表- 这决定了能够预订会话的最大参与者数量。
表 2 - 课程 - 要提供的课程、日期/时间和对 CourseSize 记录的引用。
表 3 - 预订表- 参考相关会话记录进行预订的人。
SQL如下-
CREATE TABLE [dbo].[CourseSize]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[CourseSizeMax] [tinyint] NOT NULL,
CONSTRAINT [PK_CourseSize] PRIMARY KEY CLUSTERED ([ID] ASC)
);
CREATE TABLE [dbo].[Session]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[CourseSizeID] [int] NOT NULL,
CONSTRAINT [PK_Session] PRIMARY KEY CLUSTERED ([ID] ASC)
);
GO
ALTER TABLE [dbo].[Session] WITH CHECK
ADD CONSTRAINT [FK_Session_CourseSize]
FOREIGN KEY([CourseSizeID])
REFERENCES [dbo].[CourseSize] ([ID]);
GO
ALTER TABLE [dbo].[Session]
CHECK CONSTRAINT [FK_Session_CourseSize];
GO
CREATE TABLE [dbo].[Booking]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[SessionID] [int] NOT NULL,
CONSTRAINT [PK_Booking] PRIMARY KEY CLUSTERED ([ID] ASC)
);
GO
ALTER TABLE [dbo].[Booking] WITH CHECK
ADD CONSTRAINT [FK_Booking_Session]
FOREIGN KEY([SessionID])
REFERENCES [dbo].[Session] ([ID]);
GO
ALTER TABLE [dbo].[Booking]
CHECK CONSTRAINT [FK_Booking_Session];
GO
Run Code Online (Sandbox Code Playgroud)
问题- 如果针对会话的预订数量已达到最大课程规模,我需要确保停止插入预订表。过去我使用过这样的东西:
INSERT INTO Booking
SELECT 1 AS SessionID
WHERE
(
SELECT COUNT(*)
FROM Booking
WHERE SessionID = 1
) <=
(
SELECT CourseSizeMax
FROM CourseSize
INNER JOIN Session ON CourseSize.ID = Session.CourseSizeID
WHERE Session.ID = 1
);
Run Code Online (Sandbox Code Playgroud)
当多个用户同时进行预订时,这会失败。
我通过将 CourseSizeMax 设置为 2 并使用WAITFOR time. 我安排查询运行 3 次;2 同时和其他两个之前的最后 1 毫秒。他们都能够插入一行,从而超出了会话大小限制。
我真的很想避免使用触发器来处理这个问题;我的偏好是使用约束。我可以完全控制数据库的设计,因此在需要时更改结构没有问题。
如何防止同时更新打破课程大小限制?
如果你只有两张桌子,Session 和 Booking,你可以这样做:
只要您的所有约束都受到信任,您就一切就绪。
我不知道你为什么需要第三个表 CourseSize。
您要求的是跨 2 个表的约束或带有涉及另一个表的子查询的表级约束(这是 SQL-Server 表级约束的限制)。检查CREATE TABLE语法,段落检查约束:
列级 CHECK 约束只能引用受约束的列,表级 CHECK 约束只能引用同一个表中的列。
在 SQL-92 标准中,存在ASSERTION(跨多个表的约束),如果它可用的话,实际上您会使用它。请参阅此问题中的答案:Why don't DBMS's support ASSERTION了解详细信息以及有关某些具有此类功能但有限制的产品(MS-Access)的信息。
Firebird 文档说它允许 CHECK 约束中的子查询。
在 SQL-Server 中,恐怕唯一的解决方案就是触发器。