约束只有一个logging标记为默认值
我怎么能在表上设置一个约束,以便只有一个logging的isDefault
位字段设置为1?
该约束不是表范围,而是由FormID指定的每组行的一个默认值。
这是Damien_The_Unbeliever解决scheme的修改,允许每个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)
使用唯一的过滤索引
在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的许多行,则会出现此错误:
无法在具有唯一索引“IX_TableName_FormID_isDefault”的对象“dbo.TableName”中插入重复键行。 重复键值是(1)。
资料来源: http : //technet.microsoft.com/en-us/library/cc280372.aspx
从规范化的angular度来看,这将是存储单一事实的低效方式。
我会select在更高级别保存这些信息,方法是在另一个表中存储一个被认为是默认行的标识符的外键。
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
您可以使用插入/更新触发器 。
在插入或更新后的触发器内,如果isDefault = 1的行数大于1,则回滚事务。
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设置。
我不知道SQLServer.But如果它支持Oracle中的基于函数的索引,我希望这可以翻译,如果不是,抱歉。
你可以像这样做一个索引,默认值是1234
,列是DEFAULT_COLUMN
, ID_COLUMN
是主键:
CREATE UNIQUE INDEX only_one_default ON my_table ( DECODE(DEFAULT_COLUMN, 1234, -1, ID_COLUMN) )
如果在任何其他情况下DEFAULT_COLUMN
的值是1234
和ID_COLUMN
,则此DDL创build索引为-1
的唯一索引。 然后,如果两列具有DEFAULT_COLUMN
值,则会引发exception。
这个问题意味着我有一个主表有一些子logging,其中一个子logging是默认的logging。 在这里使用地址和一个单独的默认表是一个如何使用第三范式来实现的例子。 当然,我不知道回答这么古老的东西是否有价值,但却让我感到震惊。
--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
你可以通过而不是触发器,或者如果你想它作为一个约束创build一个约束,引用一个函数,该函数检查默认设置为1的行
编辑哎呀,需要<=
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)
这是一个相当复杂的过程,不能通过简单的约束来处理。
我们通过触发来做到这一点。 但是在写触发器之前,你需要能够回答几件事情:
如果缺省值存在,我们是否想要插入失败,将其更改为0而不是1,或者将现有缺省值更改为0,并将其保留为1? 如果删除默认logging并且其他非默认logging仍然存在,我们该怎么办? 我们做一个默认的,如果是的话,我们如何确定哪一个?
您还需要非常非常小心地使触发器处理多行处理。 例如,一个客户可能会决定所有的特定types的logging应该是默认的。 您不会一次更改一百万条logging,因此此触发器需要能够处理该logging。 它也需要处理,而不循环或使用游标(你真的不希望上面讨论的事务types需要花费数小时locking整个表)。
这个触发器在上线之前还需要一个非常广泛的testing场景。 您需要testing:添加没有默认值的logging,并且是该客户添加具有默认值的logging的第一条logging,并且是该客户添加没有默认值的logging的第一条logging,并且不是第一条logging该客户添加一个带有默认logging的logging,并且不是该客户的第一个logging更新logging以在没有其他logging时具有缺省logging(假定您不需要将一个logging始终设置为缺省值)更新删除默认logging用deafult删除logging删除没有默认值的logging在数据中执行包含两个logging的多个情况下的质量插入,这两个logging的isdefault设置为1,并且运行单个logging插入时testing的所有情况执行包含两个logging的数据中的多个情况的批量更新,这两个logging都具有isdefault设置为1,以及在运行单个logging更新时testing的所有情况执行批量删除 在数据中有多种情况,包括两个logging都是默认设置为1,并且运行单个logging删除时testing的所有情况
@Andy Jones给出了最接近我的答案,但是考虑到三条规则 ,我直接把逻辑放在存储过程中,更新了这个表。 这是我简单的解决scheme。 如果我需要从其他地方更新表格,我会将逻辑移至触发器。 一个默认规则适用于由FormID和ConfigID指定的每组logging:
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