web-dev-qa-db-fra.com

Script permettant de supprimer toutes les connexions à une base de données (Plus que RESTRICTED_USER ROLLBACK)

J'ai une base de développement qui redéploie fréquemment à partir d'un projet de base de données Visual Studio (via une construction automatique TFS).

Parfois, lorsque je lance ma construction, j'obtiens cette erreur:

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.  

J'ai essayé ceci:

ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE

mais je ne peux toujours pas abandonner la base de données. (Je suppose que la plupart des développeurs ont un accès dbo.)

Je peux exécuter manuellement SP_WHO et commencer à supprimer les connexions, mais il me faut un moyen automatique de le faire dans la construction automatique. (Bien que cette fois ma connexion soit la seule sur la base de données que j'essaye de laisser tomber.)

Existe-t-il un script qui peut supprimer ma base de données indépendamment de la personne connectée?

198
Vaccano

Mise à jour

Pour MS SQL Server 2012 et supérieur

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

Pour 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

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

124
Chains

Vous pouvez obtenir le script fourni par SSMS en procédant comme suit:

  1. Cliquez avec le bouton droit sur une base de données dans SSMS et choisissez Supprimer.
  2. Dans la boîte de dialogue, cochez la case "Fermer les connexions existantes".
  3. Cliquez sur le bouton Script en haut de la boîte de dialogue.

Le script ressemblera à ceci:

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

Peu connu: l’instruction GO sql peut prendre un nombre entier correspondant au nombre de répétitions de la commande précédente.

Donc si vous:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

Ensuite:

USE [DATABASENAME]
GO 2000

Cela répète la commande USE 2000 fois, force le blocage sur toutes les autres connexions et devient propriétaire de la connexion unique. (Donner à votre fenêtre de requête le seul accès possible pour faire ce que vous voulez.)

6
Sodoshi

Le script extrêmement efficace de Matthew a été mis à jour pour utiliser le fichier DMV dm_exec_sessions, en remplacement de la table système obsolète 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;

Alternative utilisant la boucle WHILE (si vous souhaitez traiter d'autres opérations par exécution):

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

Selon mon expérience, utiliser SINGLE_USER aide la plupart du temps, cependant, il faut être prudent: j'ai eu des occasions dans lesquelles entre le moment où je lance la commande SINGLE_USER et le moment où elle est terminée ... apparemment, un autre "utilisateur" avait Accès SINGLE_USER, pas moi. Si cela se produit, vous aurez du mal à récupérer l'accès à la base de données (dans mon cas, il s'agissait d'un service spécifique exécuté pour un logiciel avec des bases de données SQL qui avait auparavant accès à l'accès SINGLE_USER). Ce que je pense devrait être le moyen le plus fiable (je ne peux pas en témoigner, mais c'est ce que je vais tester dans les jours à venir), c'est en réalité:
- arrêtez les services susceptibles d'interférer avec votre accès (le cas échéant)
- utilisez le script 'kill' ci-dessus pour fermer toutes les connexions
- définit la base de données sur single_user immédiatement après
- puis faites la restauration

3
Sacha

Vous pouvez utiliser le curseur comme ça:

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

J'ai écrit à ce sujet sur mon blog ici: http://www.pigeonsql.com/single-post/2016/12/13/Kill-all-connections-on-DB-by-Cursor

1
Filip Holub

Vous devez faire attention aux exceptions lors des processus de mise à mort. Donc, vous pouvez utiliser ce script:

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
Shahriar Khazaei

La réponse acceptée présente l'inconvénient de ne pas prendre en compte le fait qu'une base de données peut être verrouillée par une connexion qui exécute une requête impliquant des tables dans une base de données autre que celle connectée.

Cela peut être le cas si l'instance de serveur a plusieurs bases de données et si la requête directement ou indirectement (par exemple via des synonymes) utilise des tables dans plusieurs bases de données, etc.

Je trouve donc qu'il est parfois préférable d'utiliser syslockinfo pour trouver les connexions à tuer.

Ma suggestion serait donc d'utiliser la variante ci-dessous de la réponse acceptée d'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 a écrit un grand réponse . Je veux juste ajouter mes deux cents. Le code ci-dessous est entièrement basé sur la réponse de @ AlexK. La différence est que vous pouvez spécifier l'utilisateur et une heure depuis l'exécution du dernier lot (notez que le code utilise sys.dm_exec_sessions au lieu de 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)

Dans cet exemple, seul le processus de l'utilisateur usrDBTest, dont le dernier lot a été exécuté il y a plus d'une heure, sera éliminé.

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