O SQL Server não pode descartar o banco de dados <dbname> porque ele está em uso no momento ... mas nenhuma sessão é exibida

72

Quando tento soltar um banco de dados, recebo o erro "Não é possível soltar o banco de dados" dbname "porque ele está em uso no momento". No entanto, quando executo sp_who2, definitivamente não há sessões conectadas a esse banco de dados. Eu também configurei o banco de dados para single_user mode with rollback immediate.

Por que isso está acontecendo?

tuseau
fonte

Respostas:

20

Verifique se você não possui dependências, como instantâneos de banco de dados no banco de dados que deseja remover. Porém, a mensagem de erro pareceria diferente. Você tem certeza de que não há nenhum processo oculto que está se conectando ao seu banco de dados? Uma boa abordagem seria executar um script que mata todas as sessões e imediatamente após renomear o banco de dados para outro nome e, em seguida, descartá-lo.

crie um cursor com base nessa seleção:

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

problema dentro do cursor:

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

após o cursor ser fechado e desalocado:

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

go

sp_renamedb 'my_db', 'my_db_old'

go

DROP DATABASE MY_DB_OLD 
yrushka
fonte
Obrigado pelo código - isso pode funcionar. O que eu não entendo é, o que é uma sessão "oculta"? Eu teria pensado que sp_who e os outros metadados (DMVs) mostrariam todas as sessões, caso contrário, qual a utilidade deles?
precisa saber é o seguinte
Sim, normalmente você deve conseguir ver todos os ativos / não ativos por meio do sp_who ou consultar a tabela sysprocesses no master db. Por oculto, eu quis dizer um processo que se reconecta a partir de um serviço de aplicativo. Felicidades.
precisa saber é o seguinte
11
Isso é antiquado por vários motivos: (1) junções de estilo antigo (2) visualizações de compatibilidade com versões anteriores (3) um cursor e SQL dinâmico para executar um monte de comandos KILL quando um único ALTER executa (4) procedimentos obsoletos como sp_dboption.
Aaron Bertrand
11
Infelizmente, acho que isso não responde à pergunta - o questionador está perguntando por que isso está acontecendo, não como resolver. A resposta fornecida funciona, mas ainda não sei o que está me impedindo de excluir um banco de dados. A @AaronBertrand mencionou "até o Object Explorer pode ser o culpado", que acabou sendo o motivo de UM dos bancos de dados, mas como eu poderia dizer que era o Object Explorer com certeza?
precisa saber é o seguinte
isso me dá o erro "Não é possível usar KILL para matar o seu próprio processo"
nuander
80

Uma sessão conectada a outro banco de dados pode ter uma transação aberta que também afeta seu banco de dados - sp_who2 mostrará apenas um banco de dados. Também poderia ser algo tão simples quanto o Pesquisador de Objetos ou Detalhes do Pesquisador de Objetos aberto no SSMS, que novamente mostraria apenas um banco de dados em sp_who2.

Não se preocupe em tentar encontrar a sessão que é responsável; basta matá-los todos com uma declaração (e verifique se não está sua cópia do SSMS conectada, por exemplo, outra janela de consulta, Pesquisador de Objetos etc.):

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

Agora você poderá descartá-lo e fazer isso usando DDL, não a interface do usuário:

DROP DATABASE dbname;
Aaron Bertrand
fonte
11
Obrigado pela sua resposta, isso funcionou. Mas estou tendo dificuldade em viver com esta solução: por que não consigo descartar alguns bancos de dados devido a esse erro? Eu tenho alguns bancos de dados que não foram tocados por um ano e não há processo ou transação ostensível conectada a eles. Você poderia me dar algumas dicas para me ajudar a encontrar serviços, transações ou qualquer coisa relacionada a esses bancos de dados?
precisa saber é o seguinte
11
Na verdade, tudo o que eu precisava fazer era USE masterentão DROP DATABASE dbname. Aparentemente, tudo o que é necessário é apenas "usar" outra coisa, para liberar o banco de dados.
vapcguy
2
@vapcguy Isso só é verdade se a sua janela de consulta atual for a única conexão. Normalmente, esse não é o caso (e é por isso que minhas respostas indicam "e verifique se não está sua cópia do SSMS conectada").
Aaron Bertrand
20

Qual é o seu banco de dados atual quando você emite o DROPcomando? Tente o seguinte:

use master
go
drop database mydb
go

Verifique também se você está conectado como sae não dbono banco de dados que deseja soltar.

Gaius
fonte
Definitivamente, estou conectado ao mestre. Eu não deveria ter que estar conectado como sa para soltar um banco de dados. Isso me parece um bug - não está exibindo uma sessão ou acha que há uma sessão em uso, mas não existe.
precisa saber é o seguinte
3
Eu apenas fui pego com isso - tentei executar o script drop com o contexto definido no banco de dados a partir do prompt sqlcmd! Doh
JonnyRaa
18

Que tal ver o que o SSMS faz quando você usa a interface do usuário, mas diz para ele emitir um script para a ação? Aqui está o que o SSMS faz quando você clica com o botão direito do mouse no DB e escolhe Excluir, depois marque a caixa para fechar as conexões existentes:

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
fonte
... assumindo, claro, que é OK para reverter transações não confirmadas
swasheck
4
Você está descartando o banco de dados, eu diria que está tudo bem.
georgiosd
11
Isso funcionou para mim! :)
Leonardo Trimarchi
5

Eu já enfrentei essa situação muitas vezes e abaixo está o que eu faço:

Quando métodos óbvios não funcionam ... (como na sua situação):

Descubra o ID do banco de dados em sysdatabases.

Em seguida, execute - sp_lockque mostrará todos os bloqueios na instância, juntamente com spid e dbid.

Mate os spids com o dbid que você está tentando desconectar ou desconectar.

Embora o processo seja um pouco manual, ele pode ser automatizado como abaixo:

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
fonte
2

Encontrei uma resposta realmente simples no StackOverflow que funcionou pela primeira vez para mim:

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

Aqui está o SQL dessa resposta:

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
fonte