可以更改默认对象名称吗?

Geo*_*tis 4 sql-server

迟早,数据库开发人员会遇到并可能使用许多对象的命名方案,如外键、默认约束等。

让我们考虑这个例子:将所有外键命名为:
FK__[name of source table]__[name of source col]__ref__[name of master table]__[name of master column]

例如,当使用create table提及 fk 但未明确提供约束名称的语句时,可以使用什么样的参数化/外部程序来实现具有这样的默认名称?

Dav*_*oft 5

什么样的参数化/外部程序可以用来实现具有这样的默认名称,例如,当使用提到 fk 但没有明确提供约束名称的 create table 语句时?

当 DDL 不包含名称时,无法更改生成的默认名称。您唯一可以做的就是运行批处理来识别不合规的对象,并可能删除并重新创建它们。


i-o*_*one 5

如果你想阻止自动命名约束的创建,你可以使用数据库级 DDL 触发器CREATE_TABLEALTER_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_AGGFOR 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应该删除检查。