将约束添加到超过16列的唯一行

时间:2016-04-28 07:32:00

标签: sql sql-server sql-server-2008 constraints

我有几个数据库表,我需要确保一些列总是唯一的。我目前使用这样一个独特的约束:

ALTER TABLE [dbo].[MyTable] 
   ADD CONSTRAINT [AK_MyTable_Unique_Cols] 
   UNIQUE NONCLUSTERED ([Field_1] ASC, [Field_2] ASC,
                        [Field_3] ASC, [FkDeliveryId] ASC)
          WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, 
                ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO

表格如下所示。请注意,Sum不是约束的一部分。

Field_1 | Field_2 | Field_3 | FkDeliveryId | Sum
Foo     | Foo     | Bar     | 1            | 100
Foo     | Bar     | Bar     | 1            | 900
Bar     | Foo     | Foo     | 1            | 400
Bar     | Foo     | Bar     | 2            | 800 // Not unique 
Foo     | Foo     | Bar     | 2            | 600
Bar     | Foo     | Bar     | 2            | 300 // Not unique

但问题是表是通过C#动态创建的,而某些表的列数超过16列。因此,当我尝试在包含52列的表上创建约束时,我收到了此错误:

  

指数''在桌子上' dbo.MyTable'密钥列表中有52列。   索引键列列表的最大限制为16.无法创建   约束或索引。查看以前的错误。

所以现在我正在寻找另一种解决方案。我的SQL知识仅限于查询数据库,而不是限制,所以请耐心等待我。 :)

我的问题是:如何确保我的表中没有行是重复的(基于选定的列数)?即使有超过16列?

表可以具有不同数量的列,并且列可以具有不同的数据类型。

我已经看过this question并且喜欢哈希替代方案。但是当我有50多列和数百万行时,它会起作用吗?

请问hash always be unique

根据评论进行更新:

这些表用于存储导入文件的数据。我不知道文件的大小或它们有多少列。它是在预定作业中完成的,因此有关创建表的性能问题不是很重要。数据必须是持久的,但实际上只需要约束来确保每次插入时没有行应该是重复的。理论上,列可能具有varchar(max),这会导致哈希列变得非常大。

1 个答案:

答案 0 :(得分:5)

没有哈希不总是唯一的。发生哈希碰撞。

这似乎是一个奇怪的要求。通常,可以在表中的列子集上创建密钥。

绕过16列限制的通用解决方案是创建一个计算列,该列将所有列与一些不太可能在数据中出现的分隔符连接起来,然后在其上创建唯一索引。这与你的链接问题基本相同。

但是整体上有一个900字节的索引键限制。如果你需要支持任意列长度,包括可能超过这个的varchar(max)那么你就不能用声明性约束做这个,并且需要一些过程代码。您可以做的最好是在其上创建一个哈希值和一个非唯一索引,然后让您的插入过程检查任何哈希重复项以查看它们是否实际上是真正的重复项(可能在触发器中发生以确保始终检查它或ETL过程)本身 - 这可能会更有效率。)

如果您在触发器中执行此操作,则有助于向表中添加标识列。然后用于识别重复的代码将是。

SELECT *
FROM Inserted I 
JOIN BaseTable B ON I.HashValue = B.HashValue AND I.Id<> B.Id 
/* check remaining columns to see if actual differences exist in null safe way  
   http://sqlblog.com/blogs/paul_white/archive/2011/06/22/undocumented-query-plans-equality-comparisons.aspx
  */
  AND EXISTS (SELECT B.Col1, B.Col2 
              INTERSECT
              SELECT I.Col1, I.Col2)

如果以上内容返回任何违规行,并且可以回滚该事务。