it-swarm.com.de

Skript zum Beenden aller Verbindungen zu einer Datenbank (Mehr als RESTRICTED_USER ROLLBACK)

Ich habe eine Entwicklungsdatenbank, die häufig aus einem Visual Studio-Datenbankprojekt (über eine automatische TFS-Erstellung) erneut bereitgestellt wird.

Manchmal, wenn ich meinen Build starte, bekomme ich folgenden Fehler:

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.  

Ich habe es versucht:

ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE

ich kann die Datenbank jedoch immer noch nicht löschen. (Ich vermute, dass die meisten Entwickler dbo Zugriff haben.)

Ich kann SP_WHO manuell ausführen und Verbindungen beenden, aber ich benötige eine automatische Methode, um dies beim automatischen Erstellen zu tun. (Obwohl diesmal meine Verbindung die einzige in der Datenbank ist, die ich zu trennen versuche.)

Gibt es ein Skript, das meine Datenbank löschen kann, unabhängig davon, wer verbunden ist?

198
Vaccano

aktualisiert

Für MS SQL Server 2012 und höher

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

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

Ref: http://msdn.Microsoft.com/en-us/library/bb522682%28v=sql.105%29.aspx

124
Chains

Sie können das von SSMS bereitgestellte Skript folgendermaßen abrufen:

  1. Klicken Sie mit der rechten Maustaste auf eine Datenbank in SSMS und wählen Sie Löschen
  2. Aktivieren Sie im Dialogfeld das Kontrollkästchen "Bestehende Verbindungen schließen".
  3. Klicken Sie oben im Dialogfeld auf die Schaltfläche Skript.

Das Skript sieht ungefähr so ​​aus:

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

Wenig bekannt: Die SQL-Anweisung GO kann eine Ganzzahl annehmen, um den vorherigen Befehl so oft wie möglich zu wiederholen.

Also wenn du:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

Dann:

USE [DATABASENAME]
GO 2000

Dadurch wird der USE-Befehl 2000-mal wiederholt, alle anderen Verbindungen werden gesperrt, und die einzelne Verbindung wird in Besitz genommen. (Geben Sie Ihrem Abfragefenster den alleinigen Zugriff, um zu tun, was Sie möchten.)

6
Sodoshi

Matthews überaus effizientes Skript wurde aktualisiert, um die DMV dm_exec_sessions zu verwenden und die veraltete Systemtabelle sysprocesses zu ersetzen:

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;

Alternative mit WHILE-Schleife (wenn Sie andere Operationen pro Ausführung verarbeiten möchten):

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

Meiner Erfahrung nach hilft die Verwendung von SINGLE_USER in den meisten Fällen, jedoch sollte man vorsichtig sein: Ich habe Gelegenheiten erlebt, in denen zwischen dem Start des Befehls SINGLE_USER und dem Ende des Befehls ... anscheinend hat ein anderer 'Benutzer' die SINGLE_USER-Zugriff, nicht ich. In diesem Fall müssen Sie hart versuchen, den Zugriff auf die Datenbank wiederherzustellen (in meinem Fall war es ein spezifischer Dienst, der für eine Software mit SQL-Datenbanken ausgeführt wurde, die vor mir über den Zugriff SINGLE_USER verfügte). Was ich denke, sollte der zuverlässigste Weg sein (kann nicht dafür bürgen, aber es ist das, was ich in den kommenden Tagen testen werde), ist tatsächlich:
- Beenden von Diensten, die Ihren Zugriff beeinträchtigen können (falls vorhanden)
- Verwenden Sie das 'kill'-Skript, um alle Verbindungen zu schließen
- Setzen Sie die Datenbank sofort danach auf single_user
- Führen Sie dann die Wiederherstellung durch

3
Sacha

Sie können den Cursor folgendermaßen verwenden:

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

Ich habe darüber in meinem Blog hier geschrieben: http://www.pigeonsql.com/single-post/2016/12/13/Kill-all-connections-on-DB-by-Cursor

1
Filip Holub

Sie sollten vorsichtig mit Ausnahmen bei Tötungsprozessen sein. So können Sie dieses Skript verwenden:

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

Die akzeptierte Antwort hat den Nachteil, dass nicht berücksichtigt wird, dass eine Datenbank durch eine Verbindung gesperrt werden kann, die eine Abfrage ausführt, die Tabellen in einer anderen als der verbundenen Datenbank enthält.

Dies kann der Fall sein, wenn die Serverinstanz über mehrere Datenbanken verfügt und die Abfrage direkt oder indirekt (beispielsweise über Synonyme) Tabellen in mehreren Datenbanken usw. verwendet.

Daher finde ich, dass es manchmal besser ist, syslockinfo zu verwenden, um die zu tötenden Verbindungen zu finden.

Mein Vorschlag wäre daher, die folgende Variante der akzeptierten Antwort von AlexK zu verwenden:

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 hat ein tolles Antwort geschrieben. Ich möchte nur meine zwei Cent hinzufügen. Der folgende Code basiert vollständig auf der Antwort von @ AlexK. Der Unterschied besteht darin, dass Sie den Benutzer und eine Zeit angeben können, zu der der letzte Stapel ausgeführt wurde (beachten Sie, dass der Code sys.dm_exec_sessions anstelle von master..sysprocess verwendet):

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)

In diesem Beispiel wird nur der Prozess des Benutzers usrDBTest beendet, bei dem der letzte Stapel vor mehr als einer Stunde ausgeführt wurde.

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