Любой способ обойти уникальный индекс 16 столбцов макс

8

Согласно CREATE INDEXдокументации:

До 16 столбцов могут быть объединены в один составной индексный ключ.

У нас есть таблица с ~ 18 столбцами, которые должны сформировать уникальную комбинацию. Эта таблица не зависит от производительности - мы редко обновляем значения / вставляем записи. Нам просто нужно убедиться, что мы не дублируем наши записи ... и думали, что можем наложить простое ограничение уникальности.

Любые идеи? Я открыт, чтобы полностью избежать уникального индекса / ограничения, если есть лучший способ.

Ник Б
источник
4
Это какая-то таблица.
@Joe: нет ничего необычного в некоторых обстоятельствах, когда вы объединяете подобные подтипы в один. В моем случае требуется ключ из 15 столбцов вместо 50+ разных таблиц. Решение о реализации ...
ГБН
Хотя то, что вы просите, возможно, я не уверен, что это разумно. Вы не идете по проторенному пути. Таким образом, вас ждут сюрпризы. У вас больше шансов учиться на собственных ошибках, чем на чужих. В долгосрочной перспективе может быть проще попробовать более традиционный подход. Если вы разместите более подробную информацию, мы могли бы помочь с реализацией.
AK
Я знаю, что это было какое-то время, но что помешало вам просто использовать столбец идентификатора GUID?
Роберт Харви,

Ответы:

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, ...). Это может быть полезно для сканирования диапазона (индекс может использоваться для поиска по 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 11.10.11
1
@gbn Да, и именно поэтому я остановился на функции HashBytes, предложенной RBarryYoung. Тем не менее, я принял этот ответ, потому что он предоставил больше объяснения и исследования различных методов. (то есть я многому научился здесь)
Ник Б
13

Я думаю, что вам лучше бы поставить свою уникальную проверку индекса для вычисляемого столбца, который генерируется с использованием HASHBYTES('MD5', ...)комбинации из ваших 18 столбцов.

RBarryYoung
источник
2

Я столкнулся с этой проблемой, и мой старший администратор БД предложил использовать функцию проверки уникальности. Мои вставки являются относительно небольшими и редкими (~ 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, потому что один из моих типов данных был datetime, и я совершил ошибку новичка (?), Не предоставив необязательный аргумент стиля для моего Функция CONVERT при конвертации в varchar. Без стиля вы получаете следующую ошибку при попытке добавить PERSISTED UNIQUE NONCLUSTEREDограничения:

"column 'key_hash' in table 'table1' cannot be persisted because 
the column is non-deterministic."
Apoxy
источник
0

Вы можете объединить некоторые значения, чтобы создать новое уникальное значение и сохранить его в дополнение к текущим данным.

Создайте пользовательскую функцию для создания новых значений и триггер для заполнения поля при добавлении данных, тогда у вас не будет гораздо больше накладных расходов при обслуживании поля.

Комбинация двух или трех ваших полей даст вам ограничение до 16.

Тони
источник
-1 Я не согласен с идеей денормализации таблицы ради уменьшения количества столбцов.
Мэтт М
@Matt M - мне интересно узнать, почему вы проголосовали за мой ответ, если он не слишком отличается от первого предложения в принятом ответе на этот вопрос? Я также хотел бы знать, почему вы не согласны, каково было бы ваше решение?
Тони
На самом деле, ваше предложение, на самом деле, отличается от принятого решения. Вы выступаете за объединение столбцов, в то время как принятое решение выступает за создание нового столбца, который содержит объединенные значения. Ваше решение может потенциально представлять проблемы с производительностью через слишком сложные запросы, чтобы разделить полезные данные из ваших объединенных столбцов. Лично я бы поддержал решение, представленное RBarryYoung, которое использует комбинированный вычисляемый столбец HashBytes PERSISTED, помещенный в уникальный индекс. И наоборот, я проголосовал за его решение.
Мэтт М
@Matt M - Спасибо за ваше объяснение, но я сказал: «... создайте новое уникальное значение и сохраните его в дополнение к текущим данным». Я предполагал, что новый ключевой столбец будет новым полем, дополняющим существующие данные, а не заменяющим его. Я согласен с тем, что использование постоянного вычисляемого поля лучше, чем мое предложение UDF, но, по сути, мое решение было таким же.
Тони
Похоже, что я неправильно понял ваше решение, и я прошу прощения за это. При этом объединение нескольких столбцов, на мой взгляд, не такое хорошее решение, как данное решение HashBytes. Я уберу свой -1. Я еще раз прошу прощения за понимание прочитанного.
Мэтт M
0

Вы можете пойти с триггером для insert/ update. Сделайте выборку по столбцам с предложением having count(*) > 1. Если это возвращается не пустой, откат.

Бен Тул
источник
0

Вот что я бы сделал. Я бы создал триггер AFTER для INSERT, UPDATE, который выполняет ROW_NUMBER ()функцию, и разделов на все 18 ваших уникальных столбцов. Если максимальный номер строки больше единицы, выполните a ROLLBACK.

Томас Стрингер
источник