围绕唯一索引最大16列的任何方式


8

根据CREATE INDEX文档:

单个组合索引键最多可以组合16列。

我们有一个约18列的表格,需要形成一个唯一的组合。该表性能敏感-我们很少更新值/插入记录。我们只需要确保避免重复记录即可...并认为我们可以施加一个简单的唯一性约束。

有任何想法吗?如果有更好的方法,我愿意完全避免使用唯一的索引/约束。


4
那是桌子。

@Joe:在某些情况下,将相似的子类型组合为一个子类型并不罕见。在我的情况下,需要15列键,而不是50多个不同的表。实施决定……
gbn

尽管您要问的是可能的,但我不确定这是明智的。您没有遵循常规。因此,您感到惊讶。您比别人更容易从自己的错误中学到东西。从长远来看,尝试更传统的方法可能会更容易。如果您发布更多详细信息,我们将为实施提供帮助。
AK 2012年

我知道已经有一段时间了,但是是什么阻止了您仅使用GUID标识列?
罗伯特·哈维

Answers:


14

添加一个包含18个键的持久计算列,然后在计算列上创建唯一索引:

alter table t add all_keys as c1+c2+c3+...+c18 persisted;
create unique index i18 on t (all_keys);

请参见在计算列上创建索引

另一种方法是创建索引视图:

create view v 
with schemabinding
as select c1+c2+c3+...+c18 as all_keys
from dbo.t;

create unique clustered index c18 on v(all_keys);

请参阅创建索引视图

两种方法都允许部分键聚合:将c1 + c2 + c3聚合为k1,将c4 + c5 + c6聚合为k2等,然后在(k1,k2,...)上索引/创建索引视图。Thia对于范围扫描可能是有益的(索引可用于在c1 + c2 + c3上进行搜索。

当然,+在我的示例中,所有操作都是字符串聚合,实际使用的运算符取决于所有这些列的类型(即,您可能必须使用显式强制转换)。

PS。由于唯一性约束是由唯一性索引强制执行的,因此对唯一性索引的任何限制也将适用于唯一性约束:

create table t (
    c1 char(3), c2 char(3), c3 char(3), c4 char(3),
    c5 char(3), c6 char(3), c7 char(3), c8 char(3),
    c9 char(3), c10 char(3), c11 char(3), c12 char(3),
    c13 char(3), c14 char(3), c15 char(3), c16 char(3),
    c17 char(3), c18 char(3), c19 char(3), c20 char(3),
    constraint unq unique
      (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18));
go  


Msg 1904, Level 16, State 1, Line 3
The index '' on table 't' has 18 column names in index key list. 
The maximum limit for index or statistics key column list is 16.
Msg 1750, Level 16, State 0, Line 3
Could not create constraint. See previous errors.

但是,在持久化的计算列上创建约束的工作原理是:

create table t (
    c1 char(3), c2 char(3), c3 char(3), c4 char(3),
    c5 char(3), c6 char(3), c7 char(3), c8 char(3),
    c9 char(3), c10 char(3), c11 char(3), c12 char(3),
    c13 char(3), c14 char(3), c15 char(3), c16 char(3),
    c17 char(3), c18 char(3), c19 char(3), c20 char(3),
    all_c as 
        c1+c2+c3+c4+c5+c6+c7+c8+c9+c10+c11+
        c12+c13+c14+c15+c16+c17+c18 
        persisted
        constraint unq unique (all_c));
go  

显然,持久化列会占用磁盘上的空间,因此该方法可能对非常大的表不利。索引视图方法不存在此问题,它仅消耗索引空间,而不消耗计算列索引的空间。


1
当然要注意900字节索引键的限制...
gbn

1
@gbn是的,这就是为什么我最终按照RBarryYoung的建议使用HashBytes函数。但是,我接受了这个答案,因为它提供了更多的解释和对不同方法的探索。(即我在这里学到了很多东西)
Nick B


2

我遇到了这个问题,我的高级DBA建议使用唯一性检查功能。我的插入内容相对较小且很少出现(每个月初插入约1000行),我唯一关心的是强制执行唯一性。

CREATE FUNCTION dbo.fn_UQ_table1 ()  
RETURNS BIT

AS
BEGIN
      DECLARE @ResultBit BIT = 1

      IF EXISTS(
      SELECT COUNT(*)
      FROM [table1]
      GROUP BY [c1],[c2],[c3],[c4],[c5],[c6],
            [c7],[c8],[c9],[c10],[c11],[c12],
            [c13],[c14],[c15],[c16]
      HAVING COUNT(*) > 1)
      SELECT @ResultBit = 0

      RETURN      @ResultBit

END

SELECT dbo.fn_UQ_table1()

ALTER TABLE [table1]  
WITH NOCHECK ADD  
CONSTRAINT [CK_UQ] CHECK  (([dbo].[fn_UQ_table1]()=1))

@RBarryYoung,我现在还没有评论要发表,但是我对HASHBYTES解决方案遇到了麻烦,因为我的一种数据类型是日期时间,而且我犯了newbie(?)错误,没有为我的用户提供可选的style参数。转换为varchar时的CONVERT函数。如果没有样式,则尝试添加PERSISTED UNIQUE NONCLUSTERED约束时会出现以下错误:

"column 'key_hash' in table 'table1' cannot be persisted because 
the column is non-deterministic."

0

您可以合并一些值,以创建一个新的唯一值,并存储该值和当前数据。

创建一个用户定义的函数来创建新值,并创建一个触发器,以在添加数据时填充该字段,这样您就不必在维护该字段上有太多开销。

将您的两个或三个字段组合在一起将使您的访问量限制在16个以下。


-1我不同意为了减少列数而对表进行非规范化的想法。
Matt M

@Matt M-我很想知道为什么当我的答案与这个问题的已接受答案中的第一个建议相差不大时,您为什么拒绝我的答案?我也想知道您为什么不同意,您的解决方案是什么?
托尼

实际上,您的建议实际上与接受的解决方案不同。您主张合并列,而公认的解决方案则主张创建一个包含合并值的新列。您的解决方案可能会通过过于复杂的查询将有用的数据从合并的列中分离出来,从而潜在地带来性能问题。就个人而言,我会提倡RBarryYoung提出的解决方案,该解决方案利用放置在唯一索引中的组合HashBytes PERSISTED计算列。相反,我赞成他的解决方案。
Matt M

@马特米-谢谢你的解释,但我确实说过“......创建一个新的独特的价值和存储在另外,当前的数据” 我希望新的键列成为补充现有数据而不是替代现有数据的新字段。我同意使用持久性计算字段要好于我对UDF的建议,但从本质上讲,我的解决方案是相同的。
托尼

看来我误解了您的解决方案,对此我深表歉意。话虽如此,我认为,结合几个列并不是一个像HashBytes解决方案那样好的解决方案。我将收回-1。再次,我对我的阅读理解深表歉意。
Matt M

0

您可以使用insert/ 的触发器update。使用的子句对列进行选择分组having count(*) > 1。如果返回非空,请回滚。


0

这就是我要做的。我将为执行ROW_NUMBER ()功能的INSERT,UPDATE创建一个AFTER触发器,并按全部18个唯一列进行分区。如果最大行数大于一,则执行ROLLBACK

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.