如何在表上设置约束,以便只有一条记录的isDefault
位字段设置为1?
约束不是表范围,而是每个行的一个默认值,由FormID指定。
答案 0 :(得分:49)
在SQL Server 2008或更高版本上,您只需使用唯一过滤索引
即可CREATE UNIQUE INDEX IX_TableName_FormID_isDefault
ON TableName(FormID)
WHERE isDefault = 1
表格在哪里
CREATE TABLE TableName(
FormID INT NOT NULL,
isDefault BIT NOT NULL
)
例如,如果您尝试插入多个具有相同FormId且isDefault设置为1的行,则会出现此错误:
无法在对象'dbo.TableName'中插入具有唯一的重复键行 index'IX_TableName_FormID_isDefault'。重复键值为(1)。
答案 1 :(得分:9)
这是对Damien_The_Unbeliever解决方案的修改,允许每个FormID有一个默认值。
CREATE VIEW form_defaults
AS
SELECT FormID
FROM whatever
WHERE isDefault = 1
GO
CREATE UNIQUE CLUSTERED INDEX ix_form_defaults on form_defaults (FormID)
GO
但严肃的关系人员会告诉你这些信息应该放在另一张桌子里。
CREATE TABLE form
FormID int NOT NULL PRIMARY KEY
DefaultWhateverID int FOREIGN KEY REFERENCES Whatever(ID)
答案 2 :(得分:8)
从标准化的角度来看,这将是一种存储单一事实的低效方式。
我会选择在更高级别保存此信息,方法是将(在另一个表中)外键存储到被认为是默认行的标识符中。
CREATE TABLE [dbo].[Foo](
[Id] [int] NOT NULL,
CONSTRAINT [PK_Foo] PRIMARY KEY CLUSTERED
(
[Id] ASC
) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[DefaultSettings](
[DefaultFoo] [int] NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[DefaultSettings] WITH CHECK ADD CONSTRAINT [FK_DefaultSettings_Foo] FOREIGN KEY([DefaultFoo])
REFERENCES [dbo].[Foo] ([Id])
GO
ALTER TABLE [dbo].[DefaultSettings] CHECK CONSTRAINT [FK_DefaultSettings_Foo]
GO
答案 3 :(得分:3)
您可以使用insert/update trigger。
在插入或更新后的触发器中,如果isDefault = 1的行数大于1,则回滚事务。
答案 4 :(得分:2)
我不知道SQLServer.But如果它支持像Oracle中的基于函数的索引,我希望这可以翻译,如果没有,抱歉。
您可以在默认值为1234
的情况下执行此类索引,列为DEFAULT_COLUMN
且ID_COLUMN
为主键:
CREATE
UNIQUE
INDEX only_one_default
ON my_table
( DECODE(DEFAULT_COLUMN, 1234, -1, ID_COLUMN) )
如果在任何其他情况下-1
的值为DEFAULT_COLUMN
和1234
,则此DDL会创建索引为ID_COLUMN
的唯一索引。然后,如果两列具有DEFAULT_COLUMN
值,则会引发异常。
答案 5 :(得分:2)
CREATE VIEW vOnlyOneDefault
AS
SELECT 1 as Lock
FROM <underlying table>
WHERE Default = 1
GO
CREATE UNIQUE CLUSTERED INDEX IX_vOnlyOneDefault on vOnlyOneDefault (Lock)
GO
您需要为此启用正确的ANSI设置。
答案 6 :(得分:2)
这个问题意味着我有一个主表有一些子记录,其中一个子记录将是默认记录。这里使用地址和单独的默认表是如何使用第三范式进行实现的示例。当然,我不知道回答一些如此陈旧的东西是否很有价值,但它让我感到很自然。
--drop table dev.defaultAddress;
--drop table dev.addresses;
--drop table dev.people;
CREATE TABLE [dev].[people](
[Id] [int] identity primary key,
name char(20)
)
GO
CREATE TABLE [dev].[Addresses](
id int identity primary key,
peopleId int foreign key references dev.people(id),
address varchar(100)
) ON [PRIMARY]
GO
CREATE TABLE [dev].[defaultAddress](
id int identity primary key,
peopleId int foreign key references dev.people(id),
addressesId int foreign key references dev.addresses(id))
go
create unique index defaultAddress on dev.defaultAddress (peopleId)
go
create unique index idx_addr_id_person on dev.addresses(peopleid,id);
go
ALTER TABLE dev.defaultAddress
ADD CONSTRAINT FK_Def_People_Address
FOREIGN KEY(peopleID, addressesID)
REFERENCES dev.Addresses(peopleId, id)
go
insert into dev.people (name)
select 'Bill' union
select 'John' union
select 'Harry'
insert into dev.Addresses (peopleid, address)
select 1, '123 someplace' union
select 1,'work place' union
select 2,'home address' union
select 3,'some address'
insert into dev.defaultaddress (peopleId, addressesid)
select 1,1 union
select 2,3
-- so two home addresses are default now
-- try adding another default address to Bill and you get an error
select * from dev.people
join dev.addresses on people.id = addresses.peopleid
left join dev.defaultAddress on defaultAddress.peopleid = people.id and defaultaddress.addressesid = addresses.id
insert into dev.defaultaddress (peopleId, addressesId)
select 1,2
GO
答案 7 :(得分:0)
您可以通过而不是触发器来实现,或者如果您希望它作为约束创建一个约束,该约束引用一个函数来检查默认设置为1的行
编辑oops,需要&lt; =
Create table mytable(id1 int, defaultX bit not null default(0))
go
create Function dbo.fx_DefaultExists()
returns int as
Begin
Declare @Ret int
Set @ret = 0
Select @ret = count(1) from mytable
Where defaultX = 1
Return @ret
End
GO
Alter table mytable add
CONSTRAINT [CHK_DEFAULT_SET] CHECK
(([dbo].fx_DefaultExists()<=(1)))
GO
Insert into mytable (id1, defaultX) values (1,1)
Insert into mytable (id1, defaultX) values (2,1)
答案 8 :(得分:0)
这是一个相当复杂的过程,无法通过简单的约束来处理。
我们通过触发器来做到这一点。但是在你编写触发器之前,你需要能够回答几件事:
如果存在默认值,我们是否要使插入失败,将其更改为0而不是1或将现有默认值更改为0并将此值保留为1? 如果删除默认记录并且其他非默认记录仍然存在,我们该怎么办?我们是否将其设为默认值,如果是,我们如何确定哪一个?
您还需要非常非常小心地使触发器处理多行处理。例如,客户端可能会决定特定类型的所有记录都应该是默认值。您不会一次更改一百万条记录,因此此触发器需要能够处理。它还需要处理它而不需要循环或使用游标(你真的不希望上面讨论的事务类型花费数小时锁定表格。)
在触发器上线之前,您还需要一个非常广泛的测试场景。你需要测试: 添加没有默认值的记录,它是该客户的第一条记录 添加具有默认值的记录,它是该客户的第一条记录 添加没有默认值的记录,它不是该客户的第一条记录 添加具有默认值的记录,它不是该客户的第一条记录 在没有其他记录的情况下更新记录以具有默认值(假设您不需要将一条记录始终设置为deafault) 更新记录以删除默认值 用聋人删除记录 删除没有默认值的记录 在数据中执行包含多种情况的批量插入,包括两个都将默认设置为1的记录以及运行单个记录插入时测试的所有情况 在数据中执行包含多个情境的批量更新,包括两个记录,这些记录的默认值设置为1,并且在运行单个记录更新时测试的所有情况 在数据中执行包含多个情境的批量删除,包括两个默认设置为1的记录以及运行单个记录时删除的所有测试情况
答案 9 :(得分:0)
ALTER proc [dbo].[cpForm_UpdateLinkedReport]
@reportLinkId int,
@defaultYN bit,
@linkName nvarchar(150)
as
if @defaultYN = 1
begin
declare @formId int, @configId int
select @formId = FormID, @configId = ConfigID from csReportLink where ReportLinkID = @reportLinkId
update csReportLink set DefaultYN = 0 where isnull(ConfigID, @configId) = @configId and FormID = @formId
end
update
csReportLink
set
DefaultYN = @defaultYN,
LinkName = @linkName
where
ReportLinkID = @reportLinkId