SQL:我可以编写一个 CHECK 约束来使用外键验证外表中的数据吗?

发布于 2024-12-27 22:59:05 字数 1712 浏览 0 评论 0原文

我正在 SQL Server 2008 R2 和/或 SQL Azure 中设计一个测试数据库。 (到目前为止,我的所有代码都将在两者上运行。)

我有一个带有外键的表,我需要添加一个引用外部表中字段的约束。

通常我会让外部表管理它自己的验证检查,但在某些情况下这是不可能的(或不合逻辑的)。我提供了一些示例代码来显示我想要完成的任务。

CREATE TABLE CustomerOrder
(
    ID INT NOT NULL IDENTITY PRIMARY KEY,
    CustomerID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES Customer(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
    ProductID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES Product(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
    Quantity INT NOT NULL DEFAULT 1,
    IsPaid BIT NOT NULL DEFAULT 0
)
GO

CREATE TABLE RMA
(
    ID INT NOT NULL IDENTITY PRIMARY KEY,
    CustomerOrderID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES CustomerOrder(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,

    -- Add constraint to prevent RMAs from being
    -- created for orders that have not been paid.
    -- This could be a column constraint, or a table constraint.

    CHECK ( CustomerOrderID.IsPaid = 1 )

    -- ERROR: 'The multi-part identifier "CustomerOrderID.IsPaid"
    --         could not be bound.'
)
GO

在此示例中,将 CHECK 约束放入 CustomerOrder 表中是没有意义的,因为只要订单没有 RMA,CustomerOrder 表中的行就很乐意未付款。此外,CustomerOrder 表中的约束仍然需要引用 RMA 表来确认是否存在 RMA,因此仍然存在相同的问题。

我也尝试过:

CHECK (EXISTS(SELECT co.ID FROM CustomerOrder co
              WHERE co.ID=CustomerOrderID AND
                    co.IsPaid=1))
-- ERROR: 'Subqueries are not allowed in this context.
--         Only scalar expressions are allowed.'

由于这是基本数据验证的静态约束,并且永远不会被任何其他对象引用,因此我想避免将其变成标量函数或存储过程。
但是,为了避免使用标量函数,我需要在 SQL 中定义约束(最好是在部署数据库并创建表的同时)。

我可以在这里使用什么 SQL 语法来定义这种类型的约束?

I am designing a test database in SQL Server 2008 R2 and/or SQL Azure. (All of my code will run on both, so far.)

I have a table with a foreign key, and I need to add a constraint that references a field in the foreign table.

Normally I would have the foreign table manage it's own validation checks, but there are cases where that is impossible (or illogical). I've provided some sample-code that displays what I am trying to accomplish.

CREATE TABLE CustomerOrder
(
    ID INT NOT NULL IDENTITY PRIMARY KEY,
    CustomerID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES Customer(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
    ProductID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES Product(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
    Quantity INT NOT NULL DEFAULT 1,
    IsPaid BIT NOT NULL DEFAULT 0
)
GO

CREATE TABLE RMA
(
    ID INT NOT NULL IDENTITY PRIMARY KEY,
    CustomerOrderID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES CustomerOrder(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,

    -- Add constraint to prevent RMAs from being
    -- created for orders that have not been paid.
    -- This could be a column constraint, or a table constraint.

    CHECK ( CustomerOrderID.IsPaid = 1 )

    -- ERROR: 'The multi-part identifier "CustomerOrderID.IsPaid"
    --         could not be bound.'
)
GO

In this example, it doesn't make sense to put the CHECK constraint in the CustomerOrder table, because a row in the CustomerOrder table is perfectly happy being unpaid as long as there are no RMAs for the order. Furthermore, a constraint in the CustomerOrder table would still need to reference the RMA table to confirm whether there is an RMA, so the same issue remains.

I've also tried:

CHECK (EXISTS(SELECT co.ID FROM CustomerOrder co
              WHERE co.ID=CustomerOrderID AND
                    co.IsPaid=1))
-- ERROR: 'Subqueries are not allowed in this context.
--         Only scalar expressions are allowed.'

Since this is a static constraint for basic data validation, and will never be referenced by any other object, I'd like to avoid making this into a scalar function or stored procedure.
However, in order to avoid using a scalar function, I will need to define the constraint within SQL (preferrably at the same time my database is deployed and the table is created).

What SQL syntax could I use here to define this type of constraint?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

故事还在继续 2025-01-03 22:59:05

另一种选择(因为您要求不添加触发器或扩大外键的解决方案)是删除 IsPaid 列并添加另一个用于已付款订单的表:

CREATE TABLE CustomerOrder
(
    ID INT NOT NULL IDENTITY PRIMARY KEY,
    CustomerID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES Customer(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
    ProductID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES Product(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
    Quantity INT NOT NULL DEFAULT 1
)
GO

CREATE TABLE CustomerOrderPaid
(
    ID INT NOT NULL PRIMARY KEY
        FOREIGN KEY REFERENCES CustomerOrder(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE
)
GO

CREATE TABLE RMA
(
    ID INT NOT NULL IDENTITY PRIMARY KEY,
    CustomerOrderID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES CustomerOrderPaid(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
)
GO

Another option (since you asked for a solution without adding triggers or enlarging the foreign key), is removing the IsPaid column and adding another table for paid orders:

CREATE TABLE CustomerOrder
(
    ID INT NOT NULL IDENTITY PRIMARY KEY,
    CustomerID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES Customer(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
    ProductID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES Product(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
    Quantity INT NOT NULL DEFAULT 1
)
GO

CREATE TABLE CustomerOrderPaid
(
    ID INT NOT NULL PRIMARY KEY
        FOREIGN KEY REFERENCES CustomerOrder(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE
)
GO

CREATE TABLE RMA
(
    ID INT NOT NULL IDENTITY PRIMARY KEY,
    CustomerOrderID INT NOT NULL UNIQUE
        FOREIGN KEY REFERENCES CustomerOrderPaid(ID)
        ON DELETE NO ACTION ON UPDATE CASCADE,
)
GO
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文