我需要有关 SQL Server 约束的帮助。情况是对于表上的每个OrderID=1(外键不是主键所以有多个具有相同ID的行),其中一个行的位字段只能为1,并且对于OrderID=2的每一行,位字段只能为 1 一行,依此类推。对于具有相同 OrderID 的所有其他行,它应该为 0。如果已经有一行具有该 OrderID 且位字段设置为 1 的行,则位字段中带有 1 的任何新记录都应拒绝。有什么想法吗?
4 回答
CREATE UNIQUE INDEX ON UnnamedTable (OrderID) WHERE UnnamedBitField=1
它被称为过滤索引。如果您使用的是 2008 之前的 SQL Server 版本,则可以使用索引视图实现过滤索引的穷人等效项:
CREATE VIEW UnnamedView
WITH SCHEMABINDING
AS
SELECT OrderID From UnnamedSchema.UnnamedTable WHERE UnnamedBitField=1
GO
CREATE UNIQUE CLUSTERED INDEX ON UnnamedView (OrderID)
您不能真正将其作为约束来执行,因为 SQL Server 仅支持列约束和行约束。没有(非伪造)方法可以编写处理表中所有值的约束。
您可以更全面地规范模式,这将帮助您不必寻找已经设置的位,而是使用连接。您需要删除位字段并创建一个新表,例如 X,其中包含 OrderID 和表的主键,X 的主键是所有这些字段。
这意味着当您插入时,您需要插入到原始表和 X f 中,并且只有在您将表上的位设置为 1 的情况下。如果 X 中已经有一行,则插入将失败,就好像已经有一个原始行的位设置为 1。
缺点是这比您的架构占用更多空间,但更容易维护,因为您无法达到将位设置为 1 的两行的等价物。
我同意其他答案,如果您可以更改架构,那么就这样做,但如果不是,那么我认为这样的事情会做。
CREATE FUNCTION fnMyCheck
(@id INT)
RETURNS INT
AS
BEGIN
DECLARE @i INT
SELECT @i = COUNT(*)
FROM MyTable
WHERE FkCol = @id
AND BitCol = 1
RETURN @i
END
ALTER TABLE YourTable
ADD CONSTRAINT ckMyCheck CHECK (fnMyCheck(FkCol)<=1)
但是在检查约束中使用 udf 可能会出现问题,例如
编辑以添加有关此“解决方案”问题的评论:
有比您链接到的更直接的问题。
INSERT INTO YourTable(FkCol,BitCol) VALUES (1,1),(1,0)
其次是
UPDATE YourTable SET BitCol=1
成功并留下两行 FkCol=1 和 BitCol=1
唯一的方法是对父表进行子类化。您没有提到它,但这种模式的一个常见原因是从具有相同公共键值的所有行的集合中表示一个唯一的活动行。假设您的位字段表示活动订单...。然后我将创建一个名为ActiveOrders的单独表,该表将仅包含位字段设置为 1 的一行
Create Table ActiveOrders(int Orderid Primary Key Null)
和另一个包含所有行的表,它有自己唯一的主键 OrderId
Create Table AllOrders
(OrderId Integer Primary Key Not Null, ActiveOrderId Integer Not Null,
[All other data fields]
Constraint FK_AllOrders2ActiveOrder
Foreign Key(ActiveOrderId) references ActiveOrders(OrderId))
您现在甚至不再需要位字段,因为 ActiveOrders 表中的行将其标识为活动订单...仅获取活动订单(在您的方案中将位字段设置为 1 的那些) ,只需加入两个表。