2012-04-04 69 views
0

我有一个SQL Server表:如何设计数据库表来执行非重复的唯一键记录

+----+-------------+-------------------+ 
| ID | CompanyID | CompanyCode  | 
+----+-------------+-------------------+ 
| 1 | 1   | AAAA-123   | 
| 2 | 2   | BBBB-111   | 
| 3 | 1   | AAAA-123   | 
| 4 | 3   | CCCC-999   | 
| 5 | 3   | CCCC-999   | 
| 6 | 1   | AAAA-123   | 
+----+-------------+-------------------+ 

ID场的PK。

CompanyIDCompanyCode字段指定一个唯一的公司,其中一个CompanyID将始终具有相同的CompanyCode(反之亦然),并没有两个公司都不会有相同的CompanyID和/或CompanyCode

我想在桌上创建一个规则,如果/当CompanyCodeCompanyID匹配的现有CompanyCode不匹配时,永不允许将记录添加到表中。

下面是什么,我不能让餐桌上出现一个例子:

+----+-------------+-------------------+ 
| ID | CompanyID | CompanyCode  | 
+----+-------------+-------------------+ 
| 1 | 1   | AAAA-123   | 
| 2 | 1   | BBBB-111   |<<<< This record should not be allowed 
| 3 | 1   | AAAA-123   | 
| 4 | 3   | CCCC-999   | 
| 5 | 3   | CCCC-999   | 
| 6 | 1   | AAAA-123   | 
+----+-------------+-------------------+ 

通知与ID=2备案,CompanyCode BBBB-111不匹配了CompanyCode of AAAA-123现有记录。

我希望这个规则以某种方式存在于表上 - 即我不希望这个规则是查询和/或存储过程必须管理的业务规则。

我想你可以说,我想执行重复记录(上CompanyIDCompanyCode)当且仅当现有CompanyID和/或CompanyCode存在于表。

这是可能做到在表设计水平?或者我坚持不得不在我的脚本中进行管理?

更新

虽然这是不是真的有关我的OP,从我收到的反馈,我想我应该给的CompanyID /企业编码表的设计有点背景。

首先,这只是一个模拟表设计,试图解释我的问题 - 我的真正表与公司无关。

其次,我真正的表是两个web服务之间的中间人,其中服务1创建一个DeviceID和服务2 收集一个SerialNumber存在于微器件。另外,我真正的表格还包含了这个中间人服务器必须处理的许多其他列。

这个中间人服务的一个奇怪的事情是,我说的表必须允许NULLS DeviceID和SerialNumber - 这一切都取决于哪个服务先发送记录....我说的太多了,而且还没有解决我原来的问题,但我认为我必须澄清我的表格示例,因为我正在为突破的标准化问题争论不休。

+3

为什么公司代码在这里,而不是在公司的表? – 2012-04-04 12:45:59

+4

这就像您在学习规范化时显示的早期示例之一 - 如果CompanyCode依赖于CompanyID(反之亦然,在这种情况下),应该有一个表格代表该事实一次,然后只有其中一个键应该出现在上表中。 – 2012-04-04 12:56:59

回答

0

好吧,这不是最漂亮的代码,但它确实强制约束,我想。关键是要建立与它定义了两个唯一索引的索引视图:

create table dbo.ABC (
    Col1 int not null, 
    Col2 int not null 
) 
go 
create view dbo.ABC_Col1_Col2_dep 
with schemabinding 
as 
    select Col1,Col2,COUNT_BIG(*) as Cnt 
    from 
     dbo.ABC 
    group by 
     Col1,Col2 
go 
create unique clustered index IX_Col1_UniqueCol2 on dbo.ABC_Col1_Col2_dep (Col1) 
go 
create unique nonclustered index IX_Col2_UniqueCol1 on dbo.ABC_Col1_Col2_dep (Col2) 
go 

现在我们插入一些初始数据:

insert into dbo.ABC (Col1,Col2) 
select 1,3 union all 
select 2,19 union all 
select 3,12 

我们可以用完全相同的值添加其他行的Col1Col2

insert into dbo.ABC (Col1,Col2) 
select 1,3 

但是,如果我们选择一个值Col2已用于其他Col1或副反之亦然,我们得到的错误:

insert into dbo.ABC (Col1,Col2) 
select 2,3 
go 
insert into dbo.ABC (Col1,Col2) 
select 1,5 

这里的技巧是观察这个查询:

select Col1,Col2,COUNT_BIG(*) as Cnt 
    from 
     dbo.ABC 
    group by 
     Col1,Col2 

只会有一行特定Col1值,只有一排用特别是Col2的值,前提是您要强制执行的约束没有被破坏 - 但是一旦将不匹配的行插入基表中,此查询将返回多行。

4
CREATE UNIQUE INDEX idx_name ON TableName(CompanyID, CompanyCode) 

(假设您的表名为TableName)。

但是,您应该知道,此设计违反了数据库规范化原则。正确规范化的数据库将有一个单独的表格,只有CompanyID和CompanyName字段。然后,您可以修改示例中的表格以仅具有CompanyID字段;公司代码将在需要时“查找”(使用VIEW)。

这种设计具有以下优点:

  1. 变得根本不可能有不同的名称相同的公司ID。

  2. 在公司更改其代码的情况下,您只需更新单个表中的一条记录,该记录防错并且更快。

  3. 您节省了存储空间。

它有一个小缺点,就是你必须加入公司表来获取代码。在关系数据库中,这不一定是一个真正的缺点(加入标准化数据是关系数据库,),但通过消除CompanyID并将代码用作公司的主键,您甚至可以消除这种情况。如果代码是不可变的,我只会考虑这一点(对于给定的公司,它不会随时间而改变)。

+0

这将不起作用,因为唯一索引不会允许多个记录包含相同的CompanyID和CompanyCode。注意我的OP中的示例表如何包含多个具有重复ID /代码的记录。 – Jed 2012-04-04 13:26:42

2

我建议你不要在表格中存储CompanyID和CompanyCode。相反,你应该有另一个表,每个CompanyId存储1行,也有相应的代码。然后,您可以设置一个外键以确保此表中的CompanyId存在于另一个表中。

2

我不知道如何制定规则来强制执行此操作,但是您对同一事物使用了两个独立的外键(这将永远匹配)......没有公司代码会更好一些在这个表中,当你需要这些数据,或者以其他方式重新设计它时,将它加入到不同的表中。

0

您正在违反第3范式。非密钥依赖于另一个非密钥。 CompanyCode依赖于CompanyID。其他人所说的补救措施是创建一个CompanyID,CompanyCode表,其中CompanyID是PK。从您的表中删除CompanyCode并在ComanyID上创建指向您新表的FK关系。 http://www.phlonx.com/resources/nf3/ http://en.wikipedia.org/wiki/Third_normal_form