在预订系统中保持参照完整性

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 毫秒。他们都能够插入一行,从而超出了会话大小限制。

我真的很想避免使用触发器来处理这个问题;我的偏好是使用约束。我可以完全控制数据库的设计,因此在需要时更改结构没有问题。

如何防止同时更新打破课程大小限制?

A-K*_*A-K 5

如果你只有两张桌子,Session 和 Booking,你可以这样做:

  1. 将 CourseSizeMax 列添加到您的 dbo.Session 表,并在 [dbo].[Session]([ID], [CourseSizeMax]) 上添加唯一约束 - 稍后需要。
  2. 将 CourseSizeMax 列和 BookingNumber 列添加到您的 dbo.Booking 表中。
  3. 在 dbo.Booking(CourseId, CourseSizeMax) 上添加 FK 约束,参考 Session
  4. 添加支票(BookingNumber BETWEEN 1 AND CourseSizeMax)
  5. 在 dbo.Booking(CourseId, BookingNumber) 上添加唯一约束

只要您的所有约束都受到信任,您就一切就绪。

我不知道你为什么需要第三个表 CourseSize。


ype*_*eᵀᴹ 3

您要求的是跨 2 个表的约束或带有涉及另一个表的子查询的表级约束(这是 SQL-Server 表级约束的限制)。检查CREATE TABLE语法,段落检查约束

列级 CHECK 约束只能引用受约束的列,表级 CHECK 约束只能引用同一个表中的列。

在 SQL-92 标准中,存在ASSERTION(跨多个表的约束),如果它可用的话,实际上您会使用它。请参阅此问题中的答案:Why don't DBMS's support ASSERTION了解详细信息以及有关某些具有此类功能但有限制的产品(MS-Access)的信息。

Firebird 文档说它允许 CHECK 约束中的子查询。

在 SQL-Server 中,恐怕唯一的解决方案就是触发器。