SQL Server peut-il créer des collisions dans les noms de contraintes générés par le système?
Cela dépend du type de contrainte et de la version de 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
Exemple de résultats 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 |
+--------------------------+-----------+---------------+----------------------+
Exemple de résultats 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 |
+--------------------------+------------+---------------+----------------------+
Pour les contraintes par défaut, les contraintes de vérification et les contraintes de clé étrangère, les 4 derniers octets du nom généré automatiquement sont une version hexadécimale de l'ID d'objet de la contrainte. Comme ils objectid
sont garantis uniques, le nom doit également être unique. Dans Sybase aussi, ceux-ci utilisenttabname_colname_objectid
Pour les contraintes uniques et les contraintes de clé primaire utilisées par Sybase
tabname_colname_tabindid, où tabindid est une concaténation de chaîne de l'ID de table et de l'ID d'index
Cela aussi garantirait l'unicité.
SQL Server n'utilise pas ce schéma.
Dans SQL Server 2008 et 2017, il utilise une chaîne de 8 octets à la fin du nom généré par le système, mais l'algorithme a changé la façon dont les 4 derniers octets sont générés.
En 2008, les 4 derniers octets représentent un compteur d'entier signé qui est décalé du object_id
par -16000057
avec toute valeur négative enveloppant autour de max signé int. (L'importance de 16000057
est qu'il s'agit de l'incrément appliqué entre les créations successivesobject_id
). Cela garantit toujours l'unicité.
À partir de 2012, je ne vois aucun modèle entre l'id_objet de la contrainte et l'entier obtenu en traitant les 8 derniers caractères du nom comme la représentation hexadécimale d'un int signé.
Les noms de fonction dans la pile d'appels en 2017 montrent qu'il crée maintenant un GUID dans le cadre du processus de génération de nom (en 2008, je ne vois aucune mention MDConstraintNameGenerator
). Je suppose que c'est pour fournir une source de hasard. De toute évidence, il n'utilise pas l'ensemble des 16 octets du GUID dans ces 4 octets qui changent entre les contraintes cependant.
Je suppose que le nouvel algorithme a été fait pour une raison d'efficacité au détriment d'une possibilité accrue de collisions dans des cas extrêmes tels que le vôtre.
C'est tout à fait un cas pathologique car il nécessite que le préfixe du nom de table et le nom de colonne du PK (dans la mesure où cela affecte les 8 caractères précédant le 8 final) soient identiques pour des dizaines de milliers de tableaux avant qu'il ne devienne probable mais peut être reproduit assez facilement avec ci-dessous.
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 exemple exécuté sur SQL Server 2017 sur une base de données nouvellement créée a échoué en un peu plus d'une minute (après la création de 50 931 tables)
Msg 2714, niveau 16, état 30, ligne 15 Il existe déjà un objet nommé «PK__abcdefgh__3BD019A8175067CE» dans la base de données. Msg 1750, niveau 16, état 1, ligne 15 Impossible de créer une contrainte ou un index. Voir les erreurs précédentes.