SQL Server ne peut pas supprimer la base de données <nombase> car elle est actuellement utilisée… mais aucune session ne s'affiche

72

Lorsque j'essaie de supprimer une base de données, j'obtiens le message d'erreur "Impossible de supprimer la base de données" nombase "car elle est actuellement utilisée". Cependant, quand je cours sp_who2, il n'y a définitivement aucune session connectée à cette base de données. J'ai également défini la base de données sur single_user mode with rollback immediate.

Pourquoi cela arrive-t-il?

tuseau
la source

Réponses:

20

Assurez-vous de ne pas avoir de dépendances telles que des instantanés de base de données sur la base de données que vous souhaitez supprimer. Cependant, le message d'erreur ressemblerait autrement. Êtes-vous sûr qu'il n'y a pas de processus caché qui se connecte à votre base de données? Une bonne approche consisterait à exécuter un script qui tue toutes les sessions et immédiatement après, renommez la base de données sous un autre nom, puis supprimez la base de données.

créer un curseur à partir de cette sélection:

  select  d.name , convert (smallint, req_spid) As spid
      from master.dbo.syslockinfo l, 
           master.dbo.spt_values v,
           master.dbo.spt_values x, 
           master.dbo.spt_values u, 
           master.dbo.sysdatabases d
      where   l.rsc_type = v.number 
      and v.type = 'LR' 
      and l.req_status = x.number 
      and x.type = 'LS' 
      and l.req_mode + 1 = u.number
      and u.type = 'L' 
      and l.rsc_dbid = d.dbid 
      and rsc_dbid = (select top 1 dbid from 
                      master..sysdatabases 
                      where name like 'my_db')

question à l'intérieur du curseur:

SET @kill_process =  'KILL ' + @spid      
            EXEC master.dbo.sp_executesql @kill_process
                   PRINT 'killed spid : '+ @spid

une fois le curseur fermé et désalloué:

sp_dboption 'my_db', 'single user', 'TRUE'

go

sp_renamedb 'my_db', 'my_db_old'

go

DROP DATABASE MY_DB_OLD 
Yrushka
la source
Merci pour le code - cela pourrait fonctionner. Qu'est-ce que je ne comprends pas, qu'est-ce qu'une session "cachée"? J'aurais pensé que sp_who et les autres métadonnées (DMV) montreraient toutes les sessions, sinon quelle utilisation en sont-elles?
Tuseau
Oui, normalement, vous devriez pouvoir voir tous les actifs / non-actifs via sp_who ou interroger la table sysprocesses à partir de la base de données master. Par masqué, j'entendais un processus qui se reconnecte à partir d'un service d'application. À votre santé.
Yrushka
1
Celles-ci sont obsolètes pour plusieurs raisons: (1) vues de compatibilité ascendante (2) vues de compatibilité ascendante (3) un curseur et un code SQL dynamique pour exécuter un ensemble de commandes KILL lorsqu'un seul ALTER exécutera (4) des procédures dépréciées telles que sp_dboption.
Aaron Bertrand
1
Malheureusement, je ne pense pas que cela réponde à la question - le questionneur demande pourquoi cela se produit, pas comment le résoudre. La réponse fournie fonctionne, mais je ne sais toujours pas ce qui m'empêche de supprimer une base de données. @AaronBertrand a mentionné que "même l'explorateur d'objets pourrait être le coupable", ce qui a fini par être la raison d'être de l'une des bases de données, mais comment pourrais-je savoir qu'il s'agit bien de l'explorateur d'objets?
LearnByReading
cela me donne l'erreur "Impossible d'utiliser KILL pour tuer votre propre processus"
mercredi
80

Une session connectée à une autre base de données peut avoir une transaction ouverte qui affecte également votre base de données - sp_who2 n'affichera qu'une base de données. Cela pourrait également être quelque chose d'aussi simple que l'explorateur d'objets ou les détails de l'explorateur d'objets ouverts dans SSMS, qui ne montreraient qu'une seule base de données dans sp_who2.

N'essayez pas de trouver la session responsable. tuez-les tous avec une seule déclaration (et assurez-vous que ce n'est pas votre copie de SSMS qui est connectée, par exemple une autre fenêtre de requête, un explorateur d'objets, etc.):

USE master;
GO
ALTER DATABASE dbname SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO

Maintenant, vous pourrez le laisser tomber, et le faire en utilisant DDL, pas l'interface utilisateur:

DROP DATABASE dbname;
Aaron Bertrand
la source
1
Merci pour votre réponse, cela a fonctionné. Mais j’ai simplement du mal à vivre avec cette solution: pourquoi ne puis-je pas supprimer certaines bases de données à cause de cette erreur? J'ai des bases de données qui n'ont pas été touchées depuis un an et il n'y a aucun processus ou transaction ostensible qui leur est connecté. Pourriez-vous me donner des indices pour m'aider à trouver des services, des transactions ou tout ce qui est lié à ces bases de données?
LearnByReading
1
En fait, tout ce que je devais faire était USE masteralors DROP DATABASE dbname. Apparemment, tout ce qu'il faut, c'est simplement "utiliser" autre chose, pour libérer la base de données.
vapcguy
2
@vapcguy Cela n'est vrai que si votre fenêtre de requête actuelle est la seule connexion. Ce n'est généralement pas le cas (et c'est pourquoi mes réponses disent "et assurez-vous que ce n'est pas votre copie de SSMS qui est connectée").
Aaron Bertrand
20

Quelle est votre base de données actuelle lorsque vous exécutez la DROPcommande? Essaye ça:

use master
go
drop database mydb
go

Assurez-vous également que vous êtes connecté en tant que saet non dbosur la base de données que vous souhaitez supprimer.

Gaius
la source
Je suis définitivement connecté au maître. Je ne devrais pas être connecté en tant que sa pour supprimer une base de données. Cela me ressemble à un bogue - il n’affiche pas de session ou il pense qu’une session est en cours d’utilisation mais qu’elle ne l’est pas.
Tuseau
3
Je viens de me faire prendre à cela - j'ai essayé d'exécuter le script de suppression avec le contexte défini dans la base de données à partir de l'invite sqlcmd! Doh
JonnyRaa
18

Pourquoi ne pas simplement voir ce que SSMS fait lorsque vous utilisez l'interface utilisateur, mais que vous lui dites d'émettre un script pour l'action? Voici ce que fait SSMS lorsque vous cliquez avec le bouton droit de la souris sur la base de données et que vous choisissez Supprimer, puis cochez la case pour fermer les connexions existantes:

EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'yourdbname'
GO

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

USE [master]
GO

DROP DATABASE [yourdbname]
GO
Thiago Silva
la source
... en supposant, bien sûr, qu'il soit correct de revenir en arrière des transactions non validées
swasheck
4
Vous supprimez la base de données, je suppose que tout va bien.
georgiosd
1
Cela a fonctionné pour moi! :)
Leonardo Trimarchi
5

J'ai fait face à cette situation plusieurs fois et voici ce que je fais:

Lorsque des méthodes évidentes ne fonctionnent pas ..... (comme dans votre cas):

Découvrez l'ID de base de données à partir de sysdatabases.

Puis exécutez - sp_lockcela affichera tous les verrous sur l'instance avec spid et dbid.

Tuez les spids avec le dbid que vous essayez de déconnecter ou de supprimer.

Bien que le processus soit un peu manuel, il peut être automatisé comme suit:

IF OBJECT_ID('tempdb.dbo.#temp', 'U') IS NOT NULL
  DROP TABLE #temp;
create table #temp (spid int
                , dbid int
                ,ObjId bigint
                , IndId bigint
                ,Type varchar(5)
                ,resource varchar(max)
                ,Mode varchar(5)
                ,status varchar(10));
declare @dbid int
select @dbid =DB_ID(db_name())

insert into #temp
exec sp_lock

select * from #temp
where dbid = @dbid
Kin Shah
la source
2

Trouvé réponse très simple sur StackOverflow qui a fonctionné la première fois pour moi:

https://stackoverflow.com/a/7469167/261405

Voici le SQL de cette réponse:

DECLARE @DatabaseName nvarchar(50)
SET @DatabaseName = N'YOUR_DABASE_NAME'

DECLARE @SQL varchar(max)

SELECT @SQL = COALESCE(@SQL,'') + 'Kill ' + Convert(varchar, SPId) + ';'
FROM MASTER..SysProcesses
WHERE DBId = DB_ID(@DatabaseName) AND SPId <> @@SPId

--Use this to see results
SELECT @SQL 
--Uncomment this to run it
--EXEC(@SQL)
Adrian Carr
la source