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的十六进制版本。作为objectid
保证唯一的名称也必须是唯一的。在Sybase中也使用这些tabname_colname_objectid
对于唯一约束和主键约束,Sybase使用
tabname_colname_tabindid,其中tabindid是表ID和索引ID的字符串连接
这也将保证唯一性。
SQL Server不使用此方案。
在SQL Server 2008和2017中,它在系统生成的名称的末尾使用8字节字符串,但是算法已更改了如何生成该字符串的后4个字节。
在2008年,最后4个字节表示一个有符号整数计数器,该计数器从偏移量object_id
,-16000057
其中任何负值都环绕在最大有符号整数周围。(意义16000057
在于,这是在连续创建之间应用的增量object_id
)。这仍然保证了唯一性。
在2012年以来,我在约束的object_id和通过将名称的后8个字符视为带符号的int的十六进制表示形式获得的整数之间根本看不到任何模式。
2017年调用堆栈中的函数名称表明,它现在在名称生成过程中创建了一个GUID(在2008年,我没有提到MDConstraintNameGenerator
)。我想这是为了提供一些随机性来源。显然,在约束之间变化的这4个字节中,并没有使用GUID的全部16个字节。
我认为新算法是出于某种效率的原因而完成的,但在极端情况下(例如您的情况)却以增加碰撞的可能性为代价。
这是一个很病理的情况,因为它要求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行无法创建约束或索引。请参阅先前的错误。