Может ли SQL Server создавать конфликты в сгенерированных системой именах ограничений?
Это зависит от типа ограничения и версии SQL Server.
CREATE TABLE T1
(
A INT PRIMARY KEY CHECK (A > 0),
B INT DEFAULT -1 REFERENCES T1,
C INT UNIQUE,
CHECK (C > A)
)
SELECT name,
object_id,
CAST(object_id AS binary(4)) as object_id_hex,
CAST(CASE WHEN object_id >= 16000057 THEN object_id -16000057 ELSE object_id +2131483591 END AS BINARY(4)) AS object_id_offset_hex
FROM sys.objects
WHERE parent_object_id = OBJECT_ID('T1')
ORDER BY name;
drop table T1
Пример результатов 2008
+--------------------------+-----------+---------------+----------------------+
| name | object_id | object_id_hex | object_id_offset_hex |
+--------------------------+-----------+---------------+----------------------+
| CK__T1__1D498357 | 491357015 | 0x1D498357 | 0x1C555F1E |
| CK__T1__A__1A6D16AC | 443356844 | 0x1A6D16AC | 0x1978F273 |
| DF__T1__B__1B613AE5 | 459356901 | 0x1B613AE5 | 0x1A6D16AC |
| FK__T1__B__1C555F1E | 475356958 | 0x1C555F1E | 0x1B613AE5 |
| PK__T1__3BD019AE15A8618F | 379356616 | 0x169C85C8 | 0x15A8618F |
| UQ__T1__3BD019A91884CE3A | 427356787 | 0x1978F273 | 0x1884CE3A |
+--------------------------+-----------+---------------+----------------------+
Пример результатов 2017
+--------------------------+------------+---------------+----------------------+
| name | object_id | object_id_hex | object_id_offset_hex |
+--------------------------+------------+---------------+----------------------+
| CK__T1__59FA5E80 | 1509580416 | 0x59FA5E80 | 0x59063A47 |
| CK__T1__A__571DF1D5 | 1461580245 | 0x571DF1D5 | 0x5629CD9C |
| DF__T1__B__5812160E | 1477580302 | 0x5812160E | 0x571DF1D5 |
| FK__T1__B__59063A47 | 1493580359 | 0x59063A47 | 0x5812160E |
| PK__T1__3BD019AE0A4A6932 | 1429580131 | 0x5535A963 | 0x5441852A |
| UQ__T1__3BD019A981F522E0 | 1445580188 | 0x5629CD9C | 0x5535A963 |
+--------------------------+------------+---------------+----------------------+
Для ограничений по умолчанию, проверочных ограничений и ограничений внешнего ключа последние 4 байта автоматически сгенерированного имени являются шестнадцатеричной версией объекта ограничения. Поскольку objectid
гарантировано уникальное имя также должно быть уникальным. В Sybase тоже этим пользуютсяtabname_colname_objectid
Для уникальных ограничений и ограничений первичного ключа Sybase использует
tabname_colname_tabindid, где tabindid - конкатенация строк идентификатора таблицы и идентификатора индекса
Это также гарантировало бы уникальность.
SQL Server не использует эту схему.
В SQL Server 2008 и 2017 он использует 8-байтовую строку в конце имени, сгенерированного системой, однако алгоритм изменился относительно того, как генерируются последние 4 байта.
В 2008 году последние 4 байта представляют счетчик целых чисел со знаком, который смещен от object_id
by, -16000057
причем любое отрицательное значение оборачивается до максимального знакового целого числа . (Значение 16000057
заключается в том, что это приращение применяется между последовательно созданнымиobject_id
). Это по-прежнему гарантирует уникальность.
Начиная с 2012 года, я не вижу никакого паттерна между object_id ограничения и целым числом, полученным обработкой последних 8 символов имени как шестнадцатеричное представление целого числа со знаком.
Имена функций в стеке вызовов в 2017 году показывают, что теперь он создает GUID как часть процесса генерации имени (в 2008 году я не вижу упоминания MDConstraintNameGenerator
). Я предполагаю, что это должно обеспечить некоторый источник случайности. Ясно, что он не использует все 16 байтов из GUID в этих 4 байтах, которые меняются между ограничениями.
Я предполагаю, что новый алгоритм был сделан по некоторой причине эффективности за счет некоторой увеличенной вероятности столкновений в крайних случаях, таких как ваш.
Это довольно патологический случай, поскольку он требует, чтобы префикс имени таблицы и имя столбца PK (поскольку это влияет на 8 символов, предшествующих последним 8), должны быть идентичными для десятков тысяч таблиц, прежде чем это станет вероятным, но может быть воспроизведено довольно легко с ниже.
CREATE OR ALTER PROC #P
AS
SET NOCOUNT ON;
DECLARE @I INT = 0;
WHILE 1 = 1
BEGIN
EXEC ('CREATE TABLE abcdefghijklmnopqrstuvwxyz' + @I + '(C INT PRIMARY KEY)');
SET @I +=1;
END
GO
EXEC #P
Пример запуска на SQL Server 2017 для вновь созданной базы данных завершился неудачей всего за минуту (после создания 50 931 таблицы)
Сообщение 2714, уровень 16, состояние 30, строка 15 В базе данных уже есть объект с именем PK__abcdefgh__3BD019A8175067CE. Сообщение 1750, уровень 16, состояние 1, строка 15 Не удалось создать ограничение или индекс. Смотрите предыдущие ошибки.