在T-SQL中,我遇到一个约束,它将阻止为该标志插入重复记录。
对于每个[FK_Something],标志[MyFlag]
只能设置为值1,但可以多次设置为0.
这是否可能,如果不使用触发器就是如此?
我使用的是SQL Server 2008 R2。
set nocount on
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND type in (N'U'))
DROP TABLE [dbo].[MyTable]
GO
CREATE TABLE [dbo].[MyTable]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[FK_Something] [int] NOT NULL,
[MyFlag] [bit] NOT NULL
) ON [PRIMARY]
GO
-- Valid inserts --
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,1);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,1);
---------------------------------------
-- Inserts Should Fail
-- As [MyFlag] set for [FK_Something]
---------------------------------------
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,1)
GO
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,1)
GO
-- Constraint should apply this logic --
Declare @FK_Something INT
Declare @MyFlag INT
set @FK_Something = 2
Set @MyFlag = 0
if( @myflag = 0 OR
not exists(Select 1 from [MyTable] where FK_Something = @FK_Something and MyFlag = @MyFlag))
BEGIN
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (@FK_Something,@MyFlag);
print 'Inserted ';
END
GO
select * from [MyTable]
答案 0 :(得分:2)
指数。
对于每个[FK_Something],标志[MyFlag]只能设置一次 值1但可以多次设置为0。
这是1个simle索引:
这里的技巧是不对所有行应用唯一索引,这就是添加过滤器的原因。
http://msdn.microsoft.com/en-us/library/cc280372.aspx
解释了如下语法:
CREATE UNIQUE NONCLUSTERED INDEX IX_Whatever
ON MyTable (FK_Something, myFlag)
WHERE MyFlag = 1
比约束更简单并保证可以正常工作。
答案 1 :(得分:0)
您可以尝试在检查约束中创建用户定义的函数:
CREATE FUNCTION ChkFn(
@FK_Something INT
)
RETURNS INT
AS
BEGIN
DECLARE @MyFlag INT
SET @MyFlag = 0
IF( @myflag = 0 OR
NOT EXISTS( SELECT 1
FROM [MyTable]
WHERE FK_Something = @FK_Something
AND MyFlag = @MyFlag))
BEGIN
SET @retval=1;
END
RETURN @retval
END;
GO
ALTER TABLE myTable
ADD CONSTRAINT chkFkPk CHECK (dbo.ChekFn(FK_Something) = 1 );
GO
尝试一下,让我知道它是否有效。 我没有测试过这个。
请注意,对于多行更新或快照隔离可能会失败。
答案 2 :(得分:0)
感谢Mack建议使用功能。我不得不更改函数中的函数逻辑,因为它不允许插入第一个记录。我在下面发布了完整的整洁解决方案。 TomTom的索引解决方案不涉及功能,因此许多公司会更多地接受它。两者都在下面的代码中,有一个游戏,并根据您的需要选择合适的解决方案。
set nocount on
ALTER TABLE myTable drop CONSTRAINT chkFkPk
go
drop FUNCTION dbo.CheckMyTableFlag;
go
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND type in (N'U'))
DROP TABLE [dbo].[MyTable]
GO
CREATE TABLE [dbo].[MyTable]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[FK_Something] [int] NOT NULL,
[MyFlag] [bit] NOT NULL
) ON [PRIMARY]
GO
/*
CREATE FUNCTION dbo.CheckMyTableFlag(@FK_Something INT)
RETURNS INT
AS
BEGIN
Declare @retVal INT = 0
Select @retVal = sum(cast([myflag] as int)) from MyTable WHERE FK_Something = @FK_Something group by FK_Something
RETURN @retVal
END;
GO
ALTER TABLE myTable ADD CONSTRAINT chkFkPk CHECK (dbo.CheckMyTableFlag(FK_Something) <2 );
*/
go
CREATE UNIQUE NONCLUSTERED INDEX IX_Whatever
ON MyTable (FK_Something, myFlag)
WHERE MyFlag = 1
go
---- Valid inserts --
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,1);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,0);
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,1);
-----------------------------------------
---- Inserts Should Fail
---- As [MyFlag] set for [FK_Something]
-----------------------------------------
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (1,1)
GO
INSERT INTO [MyTable] ([FK_Something],[MyFlag]) VALUES (2,1)
GO
select [id],[FK_Something],[MyFlag] from mytable
go