web-dev-qa-db-ja.com

データベースへのすべての接続を切断するためのスクリプト(RESTRICTED_USER ROLLBACK以上)

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

誰が接続しているかに関わらず私のデータベースを削除できるスクリプトはありますか?

189
Vaccano

更新済み

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); 
539
AlexK
USE master
GO
ALTER DATABASE database_name
SET OFFLINE WITH ROLLBACK IMMEDIATE
GO

参照: http://msdn.Microsoft.com/ja-jp/library/bb522682%28v=sql.105%29.aspx

123
Chains

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
23
Pourya

あまり知られていません。GOSQL文は前のコマンドを繰り返す回数だけ整数を取ることができます。

あなたがそうなら:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

その後:

USE [DATABASENAME]
GO 2000

これにより、USEコマンドが2000回繰り返され、他のすべての接続でデッドロックが強制され、単一の接続の所有権が取得されます。 (あなたのクエリウィンドウにあなたが望むようにするための唯一のアクセス権を与えること。)

6
Sodoshi

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;
3
Chris Bates

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

3
Sacha

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-カーソル

1
Filip Holub

あなたは殺害プロセス中の例外について注意する必要があります。だから、あなたはこのスクリプトを使用することができます:

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

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

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

したがって、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);
1
MellowTone

@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
cantoni
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
*/
0
Emrah Saglam