¿Puede SQL Server crear colisiones en nombres de restricciones generados por el sistema?
Esto depende del tipo de restricción y la versión 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
Resultados de ejemplo 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 |
+--------------------------+-----------+---------------+----------------------+
Resultados de ejemplo 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 |
+--------------------------+------------+---------------+----------------------+
Para las restricciones predeterminadas, verifique las restricciones y las restricciones de clave externa, los últimos 4 bytes del nombre generado automáticamente son una versión hexadecimal del objectid de la restricción. Como objectid
se garantiza único, el nombre también debe ser único. En Sybase también estos usantabname_colname_objectid
Para restricciones únicas y restricciones de clave principal, Sybase utiliza
tabname_colname_tabindid, donde tabindid es una concatenación de cadenas de la ID de la tabla y la ID del índice
Esto también garantizaría la singularidad.
SQL Server no usa este esquema.
Tanto en SQL Server 2008 como en 2017 utiliza una cadena de 8 bytes al final del nombre generado por el sistema, sin embargo, el algoritmo ha cambiado en cuanto a cómo se generan los últimos 4 bytes.
En 2008 los últimos 4 bytes representan un contador entero con signo que está desplazado del object_id
por -16000057
con cualquier valor de ajuste de negativo en torno a int firmado máx. (La importancia de 16000057
es que este es el incremento aplicado entre sucesivamente creadoobject_id
). Esto todavía garantiza la unicidad.
En 2012 hacia arriba, no veo ningún patrón entre el object_id de la restricción y el entero obtenido al tratar los últimos 8 caracteres del nombre como la representación hexadecimal de un int con signo.
Los nombres de las funciones en la pila de llamadas en 2017 muestran que ahora crea un GUID como parte del proceso de generación de nombres (en 2008 no veo mención de MDConstraintNameGenerator
). Supongo que esto es para proporcionar alguna fuente de aleatoriedad. Claramente, no está usando los 16 bytes completos del GUID en esos 4 bytes que, sin embargo, cambian entre restricciones.
Supongo que el nuevo algoritmo se realizó por alguna razón de eficiencia a expensas de una mayor posibilidad de colisiones en casos extremos como el suyo.
Este es un caso bastante patológico ya que requiere que el prefijo del nombre de la tabla y el nombre de la columna de la PK (en la medida en que esto afecte a los 8 caracteres que preceden a los 8 finales) sean idénticos para decenas de miles de tablas antes de que sea probable, pero puede reproducirse bastante fácilmente con el siguiente.
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 ejemplo ejecutado en SQL Server 2017 contra una base de datos recién creada falló en poco más de un minuto (después de haber creado 50,931 tablas)
Mensaje 2714, Nivel 16, Estado 30, Línea 15 Ya existe un objeto llamado 'PK__abcdefgh__3BD019A8175067CE' en la base de datos. Msg 1750, Nivel 16, Estado 1, Línea 15 No se pudo crear restricción o índice. Ver errores anteriores.