迟早,数据库开发人员会遇到并可能使用许多对象的命名方案,如外键、默认约束等。
让我们考虑这个例子:将所有外键命名为:
FK__[name of source table]__[name of source col]__ref__[name of master table]__[name of master column]
例如,当使用create table
提及 fk 但未明确提供约束名称的语句时,可以使用什么样的参数化/外部程序来实现具有这样的默认名称?
什么样的参数化/外部程序可以用来实现具有这样的默认名称,例如,当使用提到 fk 但没有明确提供约束名称的 create table 语句时?
当 DDL 不包含名称时,无法更改生成的默认名称。您唯一可以做的就是运行批处理来识别不合规的对象,并可能删除并重新创建它们。
如果你想阻止自动命名约束的创建,你可以使用数据库级 DDL 触发器CREATE_TABLE
和ALTER_TABLE
事件来实现。
例如,例如(*)
CREATE TRIGGER [ConstraintNamingCheck] ON DATABASE
FOR CREATE_TABLE, ALTER_TABLE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @event xml, @e_type varchar(30), @s_name sysname, @o_name sysname, @o_id int, @is_filetable bit;
SET @event = EVENTDATA();
SET @e_type = @event.value('(/EVENT_INSTANCE/EventType/text())[1]', 'varchar(30)');
SET @s_name = @event.value('(/EVENT_INSTANCE/SchemaName/text())[1]', 'sysname');
SET @o_name = @event.value('(/EVENT_INSTANCE/ObjectName/text())[1]', 'sysname');
SELECT @o_id = t.object_id, @is_filetable = t.is_filetable
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE s.name = @s_name AND t.name = @o_name;
IF @is_filetable = 1
RETURN;
DECLARE @msg_constraints nvarchar(max), @msg nvarchar(max);
SELECT @msg_constraints = STRING_AGG(QUOTENAME(c.name), CHAR(13) + CHAR(10))
FROM (
SELECT name FROM sys.key_constraints WHERE parent_object_id = @o_id AND is_system_named = 1
UNION ALL
SELECT name FROM sys.foreign_keys WHERE parent_object_id = @o_id AND is_system_named = 1
UNION ALL
SELECT name FROM sys.check_constraints WHERE parent_object_id = @o_id AND is_system_named = 1
UNION ALL
SELECT name FROM sys.default_constraints WHERE parent_object_id = @o_id AND is_system_named = 1
) c(name)
WHERE @e_type = 'CREATE_TABLE'
OR @e_type = 'ALTER_TABLE'
AND @event.exist('/EVENT_INSTANCE/AlterTableActionList/Create/Constraints/Name[text()=sql:column("c.name")]') = 1;
IF @msg_constraints IS NULL OR @msg_constraints = ''
RETURN;
SET @msg = 'Table ' + QUOTENAME(@s_name) + '.' + QUOTENAME(@o_name) + ' is being '
+ LOWER(LEFT(@e_type, 5)) + 'ed with one or more auto named constraints:'
+ CHAR(13) + CHAR(10) + @msg_constraints
+ CHAR(13) + CHAR(10) + 'Please specify constraint names explicitly.';
THROW 51011, @msg, 1;
ROLLBACK TRANSACTION;
END
GO
Run Code Online (Sandbox Code Playgroud)
一旦你把它存入数据库,就像
CREATE TABLE TableName
(
id int NOT NULL,
parent_id int NULL,
name varchar(20) NOT NULL,
dateCreated datetime NOT NULL DEFAULT (GETDATE()),
value1 float NOT NULL,
value2 tinyint NOT NULL,
PRIMARY KEY (ID),
FOREIGN KEY (parent_id) REFERENCES TableName,
UNIQUE (name),
CHECK ((value1 BETWEEN 0 AND 1) AND (value2 IN (1, 2, 4, 8)))
);
Run Code Online (Sandbox Code Playgroud)
(和等价物ALTER
)将不被允许。
抛出的错误信息
消息51011,级别16,状态1,过程ConstraintNamingCheck,第45行[批量起始行50]
表[DBO] [表名]被与一个或多个自动命名约束创建:
[PK__TableNam__3213E83FB1491772]
[UQ__TableNam__72E12F1BE94335EB]
[FK__TableName__paren__316D4A39]
[CK__TableName__32616E72 ]
[DF__TableName__dateC__30792600]
请明确指定约束名称。
将提醒您或您的同事明确命名约束。可以修改它以另外建议“正确”名称。
在添加触发器之前,可以使用sp_rename规范化现有的约束名称,例如
EXEC sp_rename 'dbo.PK__TableNam__3213E83FB1491772', 'PK_TableName', 'OBJECT';
Run Code Online (Sandbox Code Playgroud)
您将必须从系统 DMV(在 DDL 触发器中使用的那些相同)获取重命名参数的信息,以获取具有is_system_named = 1
然后在循环或游标中重命名的约束。
那些想要在 SQL Server 的未来版本中禁止系统命名约束的数据库选项,例如
ALTER DATABASE [DbName]
SET CONSTRAINT_NAMING = { DEFAULT | EXPLICIT };
Run Code Online (Sandbox Code Playgroud)
可以在这里为我的倡议投票。
(*)此 DDL 触发器存在一些已知问题:
对于 2017 年之前的 SQL Server 版本,
应使用另一种字符串连接技术代替STRING_AGG
(FOR XML
例如基于方法)。
对于 SQL Server 2014 及更高版本尝试在内存优化文件组中创建表会引发Msg 12332错误
消息 12332,级别 16,状态 111,第 87 行
DDL 语句 CREATE、ALTER 和 DROP 上的数据库和服务器触发器不支持内存优化表。
解决方法是DISABLE TRIGGER
在创建内存优化表之前和ENABLE TRIGGER
之后(显然在此期间不会进行命名检查)。
对于 SQL Server 2005-2008R2 RAISERROR
应该使用而不是THROW
并且is_filetable
应该删除检查。