Czy SQL Server może tworzyć kolizje w nazwach ograniczeń generowanych przez system?
Zależy to od rodzaju ograniczenia i wersji 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
Przykładowe wyniki 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 |
+--------------------------+-----------+---------------+----------------------+
Przykładowe wyniki 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 |
+--------------------------+------------+---------------+----------------------+
W przypadku ograniczeń domyślnych, ograniczeń sprawdzania i ograniczeń klucza obcego ostatnie 4 bajty automatycznie wygenerowanej nazwy są szesnastkową wersją objectid ograniczenia. Ponieważ objectid
są one gwarantowane jako unikalne, nazwa musi być również unikalna. Również w Sybase te zastosowaniatabname_colname_objectid
W przypadku ograniczeń unikalnych i ograniczeń klucza podstawowego używa Sybase
tabname_colname_tabindid, gdzie tabindid jest ciągiem połączenia identyfikatora tabeli i identyfikatora indeksu
To także gwarantowałoby wyjątkowość.
SQL Server nie używa tego schematu.
Zarówno w programie SQL Server 2008, jak i 2017 używa 8-bajtowego ciągu na końcu nazwy generowanej przez system, jednak algorytm zmienił sposób generowania ostatnich 4 bajtów.
W 2008 r. Ostatnie 4 bajty reprezentują podpisany licznik liczb całkowitych, który jest przesunięty w stosunku do o object_id
przez -16000057
dowolne zawijanie wartości ujemnych do maksymalnej liczby całkowitej ze znakiem . (Istotne 16000057
jest to, że jest to przyrost stosowany między sukcesywnie tworzonymiobject_id
). To wciąż gwarantuje wyjątkowość.
Od 2012 roku w górę nie widzę żadnego wzorca między object_id ograniczenia a liczbą całkowitą uzyskaną przez traktowanie ostatnich 8 znaków nazwy jako szesnastkowej reprezentacji podpisanej liczby int.
Nazwy funkcji na stosie wywołań w 2017 r. Pokazują, że teraz tworzy identyfikator GUID w ramach procesu generowania nazw (w 2008 r. Nie widzę wzmianki o MDConstraintNameGenerator
). Wydaje mi się, że jest to źródło losowości. Najwyraźniej nie używa całego 16 bajtów z GUID w tych 4 bajtach, które jednak zmieniają się między ograniczeniami.
Zakładam, że nowy algorytm został wykonany z jakiegoś powodu ze względu na wydajność kosztem zwiększonej możliwości kolizji w ekstremalnych przypadkach, takich jak Twoja.
Jest to dość patologiczny przypadek, ponieważ wymaga przedrostka nazwy tabeli i nazwy kolumny PK (o ile wpływa to na 8 znaków poprzedzających ostatnie 8), aby były one identyczne dla dziesiątek tysięcy tabel, zanim stanie się prawdopodobne, ale można je całkiem odtworzyć z łatwością poniżej.
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
Przykład uruchomiony na SQL Server 2017 dla nowo utworzonej bazy danych nie powiódł się w nieco ponad minutę (po utworzeniu 50 931 tabel)
Msg 2714, poziom 16, stan 30, wiersz 15 W bazie danych znajduje się już obiekt o nazwie „PK__abcdefgh__3BD019A8175067CE”. Msg 1750, poziom 16, stan 1, wiersz 15 Nie można utworzyć ograniczenia ani indeksu. Zobacz poprzednie błędy.