【问题标题】:One Active Record per ID每个 ID 一个 Active Record
【发布时间】:2014-03-05 13:54:47
【问题描述】:
地址表为公司和联系人共享。我有一个约束,阻止用户添加带有公司 ID 和联系人 ID 的记录。我正在尝试添加另一个约束,以使每个公司 ID 或联系人 ID 只有 1 个活动 (ACTIVE FLAG = 'TRUE') 地址。但我希望能够有无限的非活动状态(ACTIVE FLAG = 'FALSE')。
ALTER TABLE [dbo].[ADDRESSES] WITH CHECK ADD CONSTRAINT [chk_ONLY_ONE_ACTIVE_ADDRESS] CHECK (([COMPANY_ID] IS NOT NULL AND [CONTACT_ID] IS NULL AND [ACTIVE] = 'TRUE' OR [COMPANY_ID] IS NULL AND [CONTACT_ID] IS NOT NULL AND [ACTIVE] = 'TRUE' OR [COMPANY_ID] IS NULL AND [CONTACT_ID] IS NOT NULL AND [ACTIVE] = 'FALSE' OR [COMPANY_ID] IS NOT NULL AND [CONTACT_ID] IS NULL AND [ACTIVE] = 'FALSE'))
GO
我在哪里错过它?
谢谢
有限的
【问题讨论】:
标签:
sql-server-2012
constraints
【解决方案1】:
您正在为联系人 ID 和公司 ID 的组合添加约束“active=true”和“active=false”,因此这是一个重言式,您也可以删除此约束。
我相信执行“只有一个活动标志和 0 到多个非活动”规则的唯一方法是通过触发器。
【解决方案2】:
感谢杰维的回复。这是我想出的解决方案。
我首先创建了两个函数...
一个
CREATE FUNCTION [dbo].[fnCheckForActiveContactAddress](
@id int
)
RETURNS INT
AS
BEGIN
DECLARE @result INT
IF @id IS NOT NULL
SET @result = ISNULL((select count(*) from dbo.Addresses where CONTACT_ID = @ID AND ACTIVE = 'TRUE'),0)
ELSE
SET @result = 1
RETURN @result
END
GO
两个
CREATE FUNCTION [dbo].[fnCheckForActiveCompanyAddress](
@id int
)
RETURNS INT
AS
BEGIN
DECLARE @result INT
If @id IS NOT NULL
SET @result = ISNULL((select count(*) from dbo.Addresses where COMPANY_ID = @ID AND ACTIVE = 'TRUE'),0)
ELSE
SET @result = 1
RETURN @result
END
GO
然后我添加了以下约束...
ALTER TABLE [dbo].[ADDRESSES] WITH CHECK ADD CONSTRAINT [chk_COMPANY_OR_CONTACT] CHECK (([COMPANY_ID] IS NOT NULL AND [CONTACT_ID] IS NULL OR [COMPANY_ID] IS NULL AND [CONTACT_ID] IS NOT NULL))
GO
ALTER TABLE [dbo].[ADDRESSES] WITH NOCHECK ADD CONSTRAINT [CHK_ADDRESSES_ONLY_ONE_ACTIVE_CONTACT] CHECK (([dbo].[fnCheckForActiveContactAddress]([CONTACT_ID])=(1)))
GO
ALTER TABLE [dbo].[ADDRESSES] WITH NOCHECK ADD CONSTRAINT [CHK_ADDRESSES_ONLY_ONE_ACTIVE_COMPANY] CHECK (([dbo].[fnCheckForActiveCompanyAddress]([COMPANY_ID])=(1)))
GO
这个解决方案似乎效果很好。
有改进的想法吗?
有限
【解决方案3】:
如果我了解您的要求,这应该可行(如果 -1 不是有效的 CompanyID 或 ContactID):
create table T (
CompanyID int,
ContactID int,
BothIDs as
CASE WHEN CompanyID IS NOT NULL and ContactID IS NOT NULL
THEN 1 ELSE 0 END PERSISTED
check (BothIDs = 0),
ActiveFlag varchar(5) check (ActiveFlag in ('TRUE','FALSE')),
ActiveCheck as
CASE WHEN ActiveFlag='TRUE' then -1 ELSE COALESCE(CompanyID,ContactID) END,
unique (ActiveCheck)
);
insert into T values
(1,NULL,'FALSE'),
(NULL,2,'FALSE'),
(3,NULL,'TRUE'),
(4,NULL,'FALSE');
GO
UPDATE T SET
ActiveFlag = 'TRUE'
WHERE CompanyID = 4;