我可以使用嵌入式业务逻辑创建高级 SQL 约束吗?

Can I Create Advanced SQL Constraints with Embedded Business Logic?

如果我有table

Widget  Version  Latest
XYZ     1.0      0
XYZ     1.1      0
XYZ     1.2      1
ABC     1.0      1

有没有办法创建一个约束

each widget-version combination can have any number of 0s for the latest version flag but must have exactly 1 occurrence where the latest flag is 1

?

或者我必须使用触发器或类似工具吗?

我看到了两种方法(第三种方法是使用视图,但它与第二种解决方案非常相似)

首先,有检查约束

有些东西(这个很丑,但你明白了):

create function [dbo].[checkLatest](@widget varchar(3))
returns bit

as
begin
declare @numOfLatest int;
declare @lastValue int;
declare @maxVersion decimal(18,2);
select @maxVersion = MAX([Version]) from Table1 where Widget = @widget;
--check if there's only one Latest = 1 by Widget
select @numOfLatest = COUNT(*) from Table1 where Widget = @widget and Latest = 1;
--check if Latest = 1 for max version
select @lastValue = Latest from Table1 where [Version] = @maxVersion and Widget = @widget;

return case when @numOfLatest = 1 and @lastValue = 1 then 1 else 0 end
end
GO

然后

ALTER TABLE Table1 
WITH CHECK ADD CONSTRAINT CK_LAtest
    CHECK (checkLatest(Widget) = 1)

注意 :当您在存储过程中将所有最新标志设置为 0 时,您必须停用约束,然后重新激活它。您可能需要锁定 table 以避免在禁用约束时进行任何插入/更新...


另一种方法 将使用计算列n 用于Latest :

create function setLatest(@Widget varchar(3), @Version decimal(18,2))
returns bit
as 
begin
declare @result bit = 0;

with cte as (select  [Version], ROW_NUMBER() over(PARTITION by [Widget] order by [Version] desc) rn from dbo.Table1 where Widget = @Widget)
select @result = case when @Version = [Version] then 1 else 0 end from 
cte where rn = 1
and @Version = [Version]
return @result;
end

然后删除您的最新专栏

alter table Table1 drop column Latest;

并将其重新添加为计算列

alter table Table1 add Latest as setLatest(Widget, [Version]);

根据计算,它永远不会出错,但是...SELECT 语句会花费更多...可能取决于您的数据大小。