データベースへのすべての接続を強制終了するスクリプト(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して接続を強制終了できますが、自動ビルドでこれを行う自動方法が必要です。(今回の接続は、ドロップしようとしているdb上の唯一のものです。)

接続しているユーザーに関係なくデータベースを削除できるスクリプトはありますか?

回答:


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
これは2つのうちのより良い答えです。データベースをオフラインにすることを避け、受け入れられた回答が常に機能するとは限りません(時にはすべてをロールバックできない場合があります)。
マークヘンダーソン2013年

3
killステートメントをすべてまとめるのにとても良い答えです。カーソルを使用して各プロセスを強制終了しますが、もちろんこれはまったく効率的ではありません。この回答で使用されているテクニックは素晴らしいです。
Saeed Neamati 2014年

マークに同意します。この方法は非常に洗練されており、データベースへの影響が少ないため、受け入れられる答えになるはずです。
オースティンS.

3
良いものと速い。問題はシステムspidのみである可能性があるため、WHERE dbid = db_id( 'My_db')およびspid> 50を追加できます
Saurabh Sinha

1
@FrenkyBスクリプトを実行する前に、データベースコンテキストを変更する必要があります。例:USE [Master]
AlexK

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、それが鍵でした。接続中にデータベースを削除しようとしていました(Duh!)。ありがとう!
ヴァッカーノ2011

9
使用するSET OFFLINE場合は、dbファイルを手動で削除する必要があります。
mattalxndr 2013年

5
alter database YourDatabaseName set SINGLE_USER with rollback immediate良いと思いませんか?これをOFFLINE(@mattalxndrの状態で)に設定すると、ファイルはディスク上に残りますがSINGLE_USER、接続は唯一のものとして残りdrop database YourDatabaseName、ファイルは削除されます。
キース

1
スクリプトの@キース、あなたはDBに接続されていないので、それは「あなたの接続」ではなく、残っている他のものです。の直後に、残りのファイルの問題を回避するためにset offline発行できset onlineます(そうです、競合状態の可能性があります)。
ivan_pozdeev 2014

2
ありがとう!このデータベースで以前に実行されたSQL Management Studioのsqlステートメントを含むタブが原因で、dbが使用中であると報告されていることに気付きませんでした。マスターを使用して、すべてを機能させる!
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
シングルユーザーモードでデータベースを使用することはお勧めしません。これにより、一部のアプリケーションユーザーへの現在の接続が失われ、それらのユーザーを見つけるために不必要に問題が発生し、同じサーバーを強制終了するか、データベースへの接続がSQLサーバーを再起動する必要がある場合があります。とても頻繁。
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でのみ認識される特別なコマンドです。
ダイスレス、2018

4

私の経験では、SINGLE_USERを使用するとほとんどの場合に役立ちますが、注意が必要です。SINGLE_USERコマンドを開始してから終了するまでの間に、別の「ユーザー」が私ではなく、SINGLE_USERアクセス。それが発生した場合、データベースへのアクセスを取り戻すのは難しい仕事です(私の場合、SQLデータベースを使用するソフトウェアで実行されていた特定のサービスであり、私が実行する前にSINGLE_USERアクセスを保持していました)。最も信頼できる方法であると私が思う(それを保証することはできませんが、これから数日間テストします)とは、実際には次のとおりです。-
アクセスを妨害する可能性のあるサービスを停止します(ある場合)
-上記の「kill」スクリプトを使用して、すべての接続を閉じます
-その直後にデータベースをsingle_userに設定します
-次に復元を行います


SINGLE_USERコマンドが(スクリプト化された)復元コマンドと同じバッチにある場合-GOステートメントで区切られていない場合!-私の経験では、他のプロセスがシングルユーザーアクセスを取得することはできません。しかし、毎晩予定されていたset-single-user; restore; set-multi-userの仕事が失敗したため、今夜は私は捕まりました。別のプロセスが私のbakファイル(smh)への排他的なファイルアクセス権を持っていたため、復元が失敗し、その後SET MULTI_USERが失敗しました...そして殺されなければなりませんでした。
ロスプレッサー2016年

3

非推奨のsysprocessesシステムテーブルに代わって、dm_exec_sessions DMVを使用するように更新されたマシューの非常に効率的なスクリプト:

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

受け入れられた回答には、接続されているもの以外のデータベース内のテーブルを含むクエリを実行している接続によってデータベースがロックされる可能性があることを考慮しないという欠点があります。

これは、サーバーインスタンスに複数のデータベースがあり、クエリが直接または(たとえば、類義語を介して)間接的に複数のデータベース内のテーブルを使用している場合などです。

したがって、killする接続を見つけるために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_locksれたsyslockinfoとマークされているテーブルを使用していますが、念のため、現在の@@ 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は素晴らしい答えを書きました。2セントを足したいだけです。以下のコードは、@ AlexKの回答に完全に基づいています。違いは、最後のバッチが実行されてからのユーザーと時間を指定できることです(コードはmaster..sysprocessの代わりにsys.dm_exec_sessionsを使用することに注意してください)。

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

あなたはそのようなカーソルを使うことができます:

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すでに1つのアクティブな接続があったときに問題が発生しました。
ivan_pozdeev 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.