SQL Server può creare collisioni nei nomi dei vincoli generati dal sistema?
Ciò dipende dal tipo di vincolo e dalla versione di 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
Risultati di esempio 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 |
+--------------------------+-----------+---------------+----------------------+
Risultati di esempio 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 |
+--------------------------+------------+---------------+----------------------+
Per i vincoli predefiniti, controllare i vincoli e i vincoli di chiave esterna, gli ultimi 4 byte del nome generato automaticamente sono una versione esadecimale dell'oggetto objectid del vincolo. Poiché objectid
sono garantiti univoci, anche il nome deve essere unico. Anche in Sybase questi usanotabname_colname_objectid
Per vincoli univoci e vincoli di chiave primaria utilizzati da Sybase
tabname_colname_tabindid, dove tabindid è una concatenazione di stringhe dell'ID tabella e dell'ID indice
Anche questo garantirebbe l'unicità.
SQL Server non utilizza questo schema.
In SQL Server 2008 e 2017 utilizza una stringa di 8 byte alla fine del nome generato dal sistema, tuttavia l'algoritmo è cambiato in base alla modalità di generazione degli ultimi 4 byte.
Nel 2008 gli ultimi 4 byte rappresentano un contatore intero con segno compensata dal object_id
da -16000057
qualsiasi involucro valore negativo intorno a max int firmata. (Il significato di 16000057
è che questo è l'incremento applicato tra successivamente creatoobject_id
). Ciò garantisce ancora unicità.
A partire dal 2012 non vedo alcun pattern tra object_id del vincolo e l'intero ottenuto trattando gli ultimi 8 caratteri del nome come la rappresentazione esadecimale di un int firmato.
I nomi delle funzioni nello stack di chiamate nel 2017 mostrano che ora crea un GUID come parte del processo di generazione del nome (Nel 2008 non vedo alcuna menzione MDConstraintNameGenerator
). Immagino che ciò fornisca una fonte di casualità. Chiaramente non sta usando tutti i 16 byte del GUID in quei 4 byte che cambiano tra i vincoli.
Presumo che il nuovo algoritmo sia stato fatto per qualche motivo di efficienza a spese di una maggiore possibilità di collisioni in casi estremi come il tuo.
Questo è un caso abbastanza patologico in quanto richiede che il prefisso del nome della tabella e il nome della colonna del PK (nella misura in cui ciò influisce sugli 8 caratteri che precedono l'8 finale) siano identici per decine di migliaia di tabelle prima che diventi probabile ma possano essere riprodotti abbastanza facilmente con il seguito.
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
Un esempio eseguito su SQL Server 2017 su un database appena creato non è riuscito in poco più di un minuto (dopo la creazione di 50.931 tabelle)
Messaggio 2714, livello 16, stato 30, riga 15 È già presente un oggetto denominato "PK__abcdefgh__3BD019A8175067CE" nel database. Messaggio 1750, livello 16, stato 1, riga 15 Impossibile creare vincolo o indice. Vedi gli errori precedenti.