用于终止与数据库的所有连接的脚本(超过RESTRICTED_USER ROLLBACK)

时间:2011-08-25 21:42:47

标签: sql sql-server tsql sql-server-2008 sql-server-2008-r2

我有一个开发数据库,​​可以从Visual Studio数据库项目中经常重新部署(通过TFS自动构建)。

有时当我运行构建时出现此错误:

ALTER DATABASE failed because a lock could not be placed on database 'MyDB'. Try again later.  
ALTER DATABASE statement failed.  
Cannot drop database "MyDB" because it is currently in use.  

我试过了:

ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE

但我仍然无法删除数据库。 (我的猜测是大多数开发人员都有dbo个访问权限。)

我可以手动运行SP_WHO并开始终止连接,但我需要一种自动方式在自动构建中执行此操作。 (虽然这次我的连接是我试图放弃的数据库中唯一的连接。)

是否存在可以删除数据库的脚本,无论谁连接?

13 个答案:

答案 0 :(得分:534)

<强>更新

对于MS SQL Server 2012及更高版本

USE [master];

DECLARE @kill varchar(8000) = '';  
SELECT @kill = @kill + 'kill ' + CONVERT(varchar(5), session_id) + ';'  
FROM sys.dm_exec_sessions
WHERE database_id  = db_id('MyDB')

EXEC(@kill);

对于MS SQL Server 2000,2005,2008

USE master;

DECLARE @kill varchar(8000); SET @kill = '';  
SELECT @kill = @kill + 'kill ' + CONVERT(varchar(5), spid) + ';'  
FROM master..sysprocesses  
WHERE dbid = db_id('MyDB')

EXEC(@kill); 

答案 1 :(得分:123)

USE master
GO
ALTER DATABASE database_name
SET OFFLINE WITH ROLLBACK IMMEDIATE
GO

参考:http://msdn.microsoft.com/en-us/library/bb522682%28v=sql.105%29.aspx

答案 2 :(得分:22)

您可以通过执行以下操作获取SSMS提供的脚本:

  1. 右键单击SSMS中的数据库,然后选择删除
  2. 在对话框中,选中“关闭现有连接”复选框。
  3. 单击对话框顶部的“脚本”按钮。
  4. 脚本看起来像这样:

    USE [master]
    GO
    ALTER DATABASE [YourDatabaseName] SET  SINGLE_USER WITH ROLLBACK IMMEDIATE
    GO
    USE [master]
    GO
    DROP DATABASE [YourDatabaseName]
    GO
    

答案 3 :(得分:6)

鲜为人知:GO sql语句可以采用整数表示重复上一个命令的次数。

所以如果你:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

然后:

USE [DATABASENAME]
GO 2000

这将重复USE命令2000次,强制所有其他连接死锁,并获得单个连接的所有权。 (让您的查询窗口可以随意访问。)

答案 4 :(得分:3)

根据我的经验,使用SINGLE_USER可以帮助大多数时间,但是,应该小心:我经历过在我开始SINGLE_USER命令和完成时间之间的时间......显然是另一个&#39 ;用户&#39;得到了SINGLE_USER,而不是我。如果发生这种情况,那么您将努力获取对数据库的访问权限(在我的情况下,这是一个特定的服务,用于运行具有SQL数据库的软件,在我执行之前获取了SINGLE_USER访问权限) )。 我认为应该是最可靠的方式(不能保证它,但它是我将在未来的日子里测试的),实际上是:
- 停止可能干扰您访问的服务(如果有的话)
- 使用&#39; kill&#39;上面的脚本关闭所有连接
- 在此之后立即将数据库设置为single_user - 然后进行恢复

答案 5 :(得分:3)

Matthew的超高效脚本已更新为使用dm_exec_sessions DMV,替换了已弃用的sysprocesses系统表:

USE [master];
GO

DECLARE @Kill VARCHAR(8000) = '';

SELECT
    @Kill = @Kill + 'kill ' + CONVERT(VARCHAR(5), session_id) + ';'
FROM
    sys.dm_exec_sessions
WHERE
    database_id = DB_ID('<YourDB>');

EXEC sys.sp_executesql @Kill;

替代使用WHILE循环(如果要执行每个执行的任何其他操作):

USE [master];
GO

DECLARE @DatabaseID SMALLINT = DB_ID(N'<YourDB>');    
DECLARE @SQL NVARCHAR(10);

WHILE EXISTS ( SELECT
                1
               FROM
                sys.dm_exec_sessions
               WHERE
                database_id = @DatabaseID )    
    BEGIN;
        SET @SQL = (
                    SELECT TOP 1
                        N'kill ' + CAST(session_id AS NVARCHAR(5)) + ';'
                    FROM
                        sys.dm_exec_sessions
                    WHERE
                        database_id = @DatabaseID
                   );
        EXEC sys.sp_executesql @SQL;
    END;

答案 6 :(得分:1)

在查杀过程中应该注意异常。所以你可以使用这个脚本:

USE master;
GO
 DECLARE @kill varchar(max) = '';
 SELECT @kill = @kill + 'BEGIN TRY KILL ' + CONVERT(varchar(5), spid) + ';' + ' END TRY BEGIN CATCH END CATCH ;' FROM master..sysprocesses 
EXEC (@kill)

答案 7 :(得分:1)

@AlexK写了一篇很棒的answer。我只想加两分钱。下面的代码完全基于@AlexK的答案,不同之处在于您可以指定用户和自上次批处理执行以来的时间(请注意,代码使用sys.dm_exec_sessions而不是master..sysprocess):

DECLARE @kill varchar(8000);
set @kill =''
select @kill = @kill + 'kill ' +  CONVERT(varchar(5), session_id) + ';' from sys.dm_exec_sessions 
where login_name = 'usrDBTest'
and datediff(hh,login_time,getdate()) > 1
--and session_id in (311,266)    
exec(@kill)

在这个例子中,只有超过1小时前执行最后一批的用户usrDBTest的进程才会被杀死。

答案 8 :(得分:1)

你可以像这样使用光标

USE master
GO

DECLARE @SQL AS VARCHAR(255)
DECLARE @SPID AS SMALLINT
DECLARE @Database AS VARCHAR(500)
SET @Database = 'AdventureWorks2016CTP3'

DECLARE Murderer CURSOR FOR
SELECT spid FROM sys.sysprocesses WHERE DB_NAME(dbid) = @Database

OPEN Murderer

FETCH NEXT FROM Murderer INTO @SPID
WHILE @@FETCH_STATUS = 0

    BEGIN
    SET @SQL = 'Kill ' + CAST(@SPID AS VARCHAR(10)) + ';'
    EXEC (@SQL)
    PRINT  ' Process ' + CAST(@SPID AS VARCHAR(10)) +' has been killed'
    FETCH NEXT FROM Murderer INTO @SPID
    END 

CLOSE Murderer
DEALLOCATE Murderer

我在博客中写到: http://www.pigeonsql.com/single-post/2016/12/13/Kill-all-connections-on-DB-by-Cursor

答案 9 :(得分:1)

被接受的答案的缺点在于,它没有考虑到数据库可以被执行查询的连接锁定,该查询涉及数据库中除连接的表之外的表。

如果服务器实例具有多个数据库并且查询直接或间接(例如通过同义词)使用多个数据库中的表,则可能是这种情况。

因此,我发现有时使用syslockinfo查找要杀死的连接会更好。

因此,我的建议是使用AlexK接受的答案的以下变体形式:

USE [master];

DECLARE @kill varchar(8000) = '';  
SELECT @kill = @kill + 'kill ' + CONVERT(varchar(5), req_spid) + ';'  
FROM master.dbo.syslockinfo
WHERE rsc_type = 2
AND rsc_dbid  = db_id('MyDB')

EXEC(@kill);

答案 10 :(得分:0)

SELECT
    spid,
    sp.[status],
    loginame [Login],
    hostname, 
    blocked BlkBy,
    sd.name DBName, 
    cmd Command,
    cpu CPUTime,
    memusage Memory,
    physical_io DiskIO,
    lastwaittype LastWaitType,
    [program_name] ProgramName,
    last_batch LastBatch,
    login_time LoginTime,
    'kill ' + CAST(spid as varchar(10)) as 'Kill Command'
FROM master.dbo.sysprocesses sp 
JOIN master.dbo.sysdatabases sd ON sp.dbid = sd.dbid
WHERE sd.name NOT IN ('master', 'model', 'msdb') 
--AND sd.name = 'db_name' 
--AND hostname like 'hostname1%' 
--AND loginame like 'username1%'
ORDER BY spid

/* If a service connects continously. You can automatically execute kill process then run your script:
DECLARE @sqlcommand nvarchar (500)
SELECT @sqlcommand = 'kill ' + CAST(spid as varchar(10))
FROM master.dbo.sysprocesses sp 
JOIN master.dbo.sysdatabases sd ON sp.dbid = sd.dbid
WHERE sd.name NOT IN ('master', 'model', 'msdb') 
--AND sd.name = 'db_name' 
--AND hostname like 'hostname1%' 
--AND loginame like 'username1%'
--SELECT @sqlcommand
EXEC sp_executesql @sqlcommand
*/

答案 11 :(得分:0)

USE MASTER
GO
 
DECLARE @Spid INT
DECLARE @ExecSQL VARCHAR(255)
 
DECLARE KillCursor CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT  DISTINCT SPID
FROM    MASTER..SysProcesses
WHERE   DBID = DB_ID('dbname')
 
OPEN    KillCursor
 
-- Grab the first SPID
FETCH   NEXT
FROM    KillCursor
INTO    @Spid
 
WHILE   @@FETCH_STATUS = 0
    BEGIN
        SET     @ExecSQL = 'KILL ' + CAST(@Spid AS VARCHAR(50))
 
        EXEC    (@ExecSQL)
 
        -- Pull the next SPID
        FETCH   NEXT 
        FROM    KillCursor 
        INTO    @Spid  
    END
 
CLOSE   KillCursor
 
DEALLOCATE  KillCursor

答案 12 :(得分:-1)

我已使用下面的简单代码成功测试

USE [master]
GO
ALTER DATABASE [YourDatabaseName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO