永久分离数据库

csp*_*ell 10 sql-server maintenance sql-server-2014

如果数据库与实例永久分离,是否应该完成任何清理任务?

Joh*_*ner 13

如果从实例中分离数据库,则需要对文件执行操作系统级别的删除。更安全的方法是删除数据库。

我的建议是在将数据库置于只读模式后对其进行最终备份(因为这将确保备份期间没有活动发生),然后通过Drop Database命令将其从系统中删除

完整的命令集将类似于以下内容:

-- Use master db to ensure you don't have an active connection to the db you wish to affect
USE [master]
GO

-- This will kill any active transactions, but will force the database into a Read-Only state
ALTER DATABASE [db_name] SET READ_ONLY WITH ROLLBACK IMMEDIATE
GO

BACKUP DATABASE [db_name] -- Fill in more options here or use the UI to take a backup if you chooose
GO

-- This will kick out all connections from the database allowing you to drop it.
ALTER DATABASE [db_name] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO

-- Drop the database (which automatically removes the files from the OS)
DROP DATABASE [db_name]
GO
Run Code Online (Sandbox Code Playgroud)

在此之后,您将需要查找针对数据库运行脚本的任何作业。我建议您等待看看什么失败了(之后您可以编写脚本/删除作业),因为作业可以通过多种方式引用数据库(并非所有这些方式都很容易识别)。

最后,您需要从实例中删除所有只能访问该数据库的用户。这个脚本应该识别那些用户是谁,尽管 Max 的版本更清晰(我没有意识到他发布了一种方法,直到我编辑了我的答案以包含这个):

DECLARE @ExecString NVARCHAR (4000)

-- Create Empty Table in a very lazy manner
SELECT  name, principal_id, CAST('' AS NVARCHAR(128)) as database_name
INTO ##tmp_AllDBUsers
FROM sys.server_principals
WHERE 1 = 2

-- Declare Cursor to iterate through all DBs on the instance
DECLARE dbCursor CURSOR
FOR
        SELECT name
        FROM sys .databases


DECLARE @name NVARCHAR (128)
OPEN dbCursor
FETCH NEXT FROM dbCursor
INTO @name

WHILE @@FETCH_STATUS = 0
BEGIN

    SET @ExecString = 
    'USE [' + @name + '];
    INSERT INTO ##tmp_AllDBUsers
    SELECT sp.name, sp.principal_id, DB_NAME()
    FROM sys.server_principals sp INNER JOIN sys.database_principals dp
        ON sp.sid = dp.sid'

    EXEC(@ExecString)

    FETCH NEXT FROM dbCursor
    INTO @name
END

-- Close and deallocate the cursor because you've finished traversing all it's data
CLOSE dbCursor
DEALLOCATE dbCursor

-- Show all logins that do not belong to a server-level role nor have access to any databases
SELECT sp.*
FROM sys.server_principals sp LEFT JOIN ##tmp_AllDBUsers adu
    ON sp.principal_id = adu.principal_id
WHERE adu.principal_id IS NULL
    AND sp.principal_id NOT IN (SELECT member_principal_id
                            FROM sys.server_role_members)
    AND TYPE IN ('S', 'U', 'G')

-- cleanup
DROP TABLE ##tmp_AllDBUsers
Run Code Online (Sandbox Code Playgroud)


Han*_*non 13

我赞成约翰的回答;我只想添加一些有关您可能想要清理的其他项目的详细信息。

  1. SQL Server 代理作业和警报可能会引用数据库。清理它们将防止报告不必要的错误。

  2. 删除任何专门为数据库创建的登录名。以下 T-SQL 将识别可能的候选登录名,您可能会调查这些登录名以查看它们是否正在被使用。该代码标识未被任何数据库引用的登录。

    DECLARE @cmd nvarchar(max);
    SET @cmd = '    SELECT sp.sid
        FROM master.sys.server_principals sp
    ';
    SELECT @cmd = @cmd + '  EXCEPT 
        SELECT dp.sid
        FROM ' + QUOTENAME(d.name) + '.sys.database_principals dp
    '
    FROM sys.databases d
    WHERE d.[state] <> 6; --ignore offline DBs
    
    SET @cmd = 'SELECT spr.*
    FROM (
    ' + @cmd + '
    ) src
        INNER JOIN master.sys.server_principals spr
            ON src.sid = spr.sid
    WHERE spr.type <> ''R''
        AND spr.name NOT LIKE ''%##MS_%''
        AND spr.name NOT LIKE ''NT %''
        AND NOT EXISTS (
            SELECT 1
            FROM sys.server_role_members srm
            WHERE srm.member_principal_id = spr.principal_id
                )
    ORDER BY spr.name;
    ';
    EXEC sys.sp_executesql @cmd;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 该数据库可能存在备份设备。虽然删除它们不是绝对必要的,但如果它们没有被使用,它们应该去消除潜在的未来混淆。

  4. 服务器级触发器可以引用数据库。

  5. 查找引用数据库的维护计划 - 如果没有更新它们以删除丢失的数据库,这些维护计划将失败。