杀死与数据库的所有连接的脚本(超过RESTRICTED_USER ROLLBACK)


239

我有一个开发数据库,​​该数据库经常从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并开始终止连接,但是我需要一种自动方式来自动构建。(尽管这次我的连接是我要删除的数据库上的唯一连接。)

是否有一个脚本可以删除我的数据库,而不管连接了谁?

Answers:


642

更新

对于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); 

25
这是两者中更好的答案。避免使数据库脱机,并且接受的答案并非总是有效(有时无法将所有内容回滚)。
马克·亨德森

3
对于汇总kill语句来说,这是一个很好的答案。我将使用游标杀死每个进程,这当然根本没有效率。此答案中使用的技术非常出色。
Saeed Neamati 2014年

我同意马克的看法。该方法应该是公认的答案,因为它明显更优雅,对数据库的影响也较小。
奥斯汀S.

3
好又快。唯一的问题可能是系统spid,因此您可以添加WHERE dbid = db_id('My_db')且spid> 50
Saurabh Sinha

1
@FrenkyB您需要在运行脚本之前更改数据库上下文。例如:USE [Master]
AlexK 2015年

133
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


9
奇怪的是,这就是USE master关键。我试图在连接到数据库时删除该数据库(Du!)。谢谢!
瓦卡诺2011年

9
如果使用SET OFFLINE,则必须手动删除数据库文件。
mattalxndr

5
alter database YourDatabaseName set SINGLE_USER with rollback immediate更好吗?如果将其设置为OFFLINE(如@mattalxndr所述),文件将保留在磁盘上,但与SINGLE_USER您的连接将保留为唯一drop database YourDatabaseName的文件,并且仍会删除文件。
Keith

1
@Keith在脚本中,您没有连接到数据库,因此它不是“您的连接”,而是其他一些。紧接在之后set offline,您可以发出命令set online来避免剩余文件的问题(是的,有竞争条件的可能性)。
ivan_pozdeev 2014年

2
谢谢!我没有意识到在此数据库上较早执行的SQL Management Studio中带有sql语句的选项卡导致我的数据库被报告使用。使用主人,去使一切正常!
eythort 2014年

26

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

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

该脚本将如下所示:

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

3
我不建议任何用户以单用户模式使用databse,因为这可能会导致您失去与某些应用程序用户的当前连接,并且不必费心查找这些用户并杀死相同或某些时间(如果与db的连接是必需的,则必须重新启动sql server)如此频繁。
Saurabh Sinha 2014年

7

鲜为人知:GO sql语句可以使用整数来重复上一个命令。

因此,如果您:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

然后:

USE [DATABASENAME]
GO 2000

这将重复USE命令2000次,在所有其他连接上强制死锁,并获得单个连接的所有权。(为查询窗口提供唯一的访问权以执行您希望的操作。)


2
GO不是TSQL命令,而是仅由sqlcmd和osql实用程序以及SSMS识别的特殊命令。
diceless

4

以我的经验,使用SINGLE_USER在大多数情况下会有所帮助,但是,应该小心一点:我经历了一些情况,在我启动SINGLE_USER命令的时间到完成它的时间之间……显然,另一个“用户”已经SINGLE_USER访问权限,不是我。如果发生这种情况,您将很难获得对数据库的访问权(在我的情况下,这是为具有SQL数据库的软件运行的一项特定服务,该软件在我获得之前拥有SINGLE_USER访问权限)。我认为应该是最可靠的方式(无法保证,但是我将在以后的日子中对其进行测试)实际上是:
-停止可能干扰您访问的服务(如果有的话)
-使用上面的“ kill”脚本关闭所有连接
-之后立即将数据库设置为single_user-
然后执行还原


如果SINGLE_USER命令与您的(脚本化)还原命令处于同一批处理中,则不要用GO语句分隔!-根据我的经验,没有其他进程可以抓住单个用户的访问权限。但是,我今晚被捕是因为我设置单用户;还原;设置多用户的夜间计划工作被炸掉了。另一个进程具有对我的bak文件(smh)的独占文件访问权限,因此还原失败,然后SET MULTI_USER失败...表示当我在半夜打电话给我清理血液时,其他人具有SINGLE_USER访问权限不得不被杀死
Ross Presser

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;

2

可接受的答案具有一个缺点,即它不考虑可以通过执行查询的连接锁定数据库,该查询涉及数据库中除连接的表以外的表。

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

因此,我发现有时最好使用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);

这个!尽管我个人使用的sys.dm_tran_lockssyslockinfo已标记为过时,但是,为防万一,您可能希望排除当前的@@ SPID。
deroby

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)

1

@AlexK写了一个很好的答案。我只想加两分钱。下面的代码完全基于@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的进程。


1

您可以像这样使用Cursor

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


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
*/

-1

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

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

2
set SINGLE_USER当存在单个活动连接时,我遇到了问题。
ivan_pozdeev 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.