SQL Server не может удалить базу данных <имя_базы>, поскольку она в данный момент используется ... но сеансы не отображаются

72

Когда я пытаюсь удалить базу данных, я получаю сообщение об ошибке «Невозможно удалить базу данных« dbname », потому что она в данный момент используется». Тем не менее, когда я запускаю sp_who2, определенно нет сеансов, связанных с этой базой данных. Я также установил базу данных single_user mode with rollback immediate.

Почему это происходит?

tuseau
источник

Ответы:

20

Убедитесь, что у вас нет зависимостей, таких как снимки базы данных, в БД, которую вы хотите удалить. Хотя сообщение об ошибке выглядело бы иначе. Вы уверены, что нет скрытого процесса, который подключается к вашей базе данных? Хорошим подходом было бы запустить скрипт, который убивает все сеансы и сразу же после переименования базы данных на другое имя, а затем удалить базу данных.

создать курсор на основе этого выбора:

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

проблема внутри курсора:

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

после того, как курсор закрыт и освобожден:

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

go

sp_renamedb 'my_db', 'my_db_old'

go

DROP DATABASE MY_DB_OLD 
yrushka
источник
Спасибо за код - это может сработать. Я не понимаю, что такое «скрытый» сеанс? Я бы подумал, что sp_who и другие метаданные (DMV) будут показывать все сеансы, иначе как они будут использоваться?
tuseau
Да, обычно вы должны видеть все активные / неактивные через sp_who или запрашивать таблицу sysprocesses из master db. Под скрытым я подразумевал процесс, который переподключается со службой приложения. Приветствия.
Ёрушка
1
Это устарело по нескольким причинам: (1) объединения в старом стиле (2) представления обратной совместимости (3) курсор и динамический SQL для запуска набора команд KILL, когда один ALTER выполнит (4) устаревшие процедуры, такие как sp_dboption.
Аарон Бертран
1
К сожалению, я не думаю, что это отвечает на вопрос - спрашивающий спрашивает, почему это происходит, а не как решить. Приведенный ответ работает, но я до сих пор не знаю, что мешает мне удалить базу данных. @AaronBertrand упомянул, что «даже Object Explorer мог быть виновником», который фактически стал причиной ОДНОЙ базы данных, но как я могу точно сказать, что это Object Explorer?
LearnByReading
это дает мне ошибку «Невозможно использовать KILL, чтобы убить ваш собственный процесс»
nuander
80

Сеанс, подключенный к другой базе данных, может иметь открытую транзакцию, которая также влияет на вашу базу данных - sp_who2 покажет только одну базу данных. Это также может быть что-то такое же простое, как Object Explorer или Object Explorer Details, открытые в SSMS, которые снова будут показывать только одну базу данных в sp_who2.

Не пытайтесь найти ответственную сессию; просто убейте их всех одним оператором (и убедитесь, что подключена не ваша копия SSMS, например, другое окно запроса, обозреватель объектов и т. д.):

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

Теперь вы сможете удалить его и сделать это с помощью DDL, а не пользовательского интерфейса:

DROP DATABASE dbname;
Аарон Бертран
источник
1
Спасибо за ваш ответ, это сработало. Но мне просто трудно жить с этим решением: почему я не могу удалить некоторые базы данных из-за этой ошибки? У меня есть несколько баз данных, которые не были затронуты в течение года, и к ним не было подключено ни одного процесса или предполагаемой транзакции. Не могли бы вы дать мне несколько советов, которые помогут мне найти потенциальные услуги или транзакции или что-нибудь, что связано с этими базами данных?
LearnByReading
1
На самом деле, все, что мне нужно было сделать USE master, это тогда DROP DATABASE dbname. Очевидно, все, что нужно, это просто «использовать» что-то еще, чтобы освободить БД.
vapcguy
2
@vapcguy Это верно только в том случае, если текущее окно запроса является единственным подключением. Обычно это не так (и именно поэтому в моих ответах говорится «и убедитесь, что подключена не ваша копия SSMS»).
Аарон Бертран
20

Какая у вас база данных, когда вы вводите DROPкоманду? Попробуй это:

use master
go
drop database mydb
go

Также убедитесь, что вы подключены, а saне dboк той базе данных, которую хотите удалить.

Gaius
источник
Я определенно связан с мастером. Мне не нужно было подключаться как sa, чтобы удалить базу данных. Это выглядит для меня как ошибка - он не отображает сеанс или думает, что сеанс используется, но его нет.
tuseau
3
Я только что поймал это - попытался запустить сценарий сброса с контекстом, установленным для базы данных, из приглашения sqlcmd! Doh
JonnyRaa
18

Как насчет того, чтобы увидеть, что делает SSMS, когда вы используете пользовательский интерфейс, но сказать ему, чтобы он выдал сценарий для действия? Вот что делает SSMS, когда вы щелкаете правой кнопкой мыши по БД и выбираете Удалить, затем устанавливаете флажок, чтобы закрыть существующие соединения:

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
Тиаго Сильва
источник
... если, конечно, можно откатывать незафиксированные транзакции
swasheck
4
Вы отбрасываете базу данных, я бы предположил, что все в порядке.
Георгиосд
1
Это сработало для меня! :)
Леонардо Тримарчи
5

Я сталкивался с этой ситуацией много раз, и вот что я делаю ниже:

Когда очевидные методы не работают ..... (как в вашей ситуации):

Узнайте идентификатор базы данных из системной базы данных.

Затем выполните - sp_lockэто покажет все блокировки на экземпляре вместе с spid и dbid.

Убейте спидов с помощью dbid, который вы пытаетесь отключить или сбросить.

Хотя этот процесс немного ручной, его можно автоматизировать, как показано ниже:

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
Кин Шах
источник
2

Нашел действительно простой ответ на StackOverflow, который работал для меня впервые:

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

Вот SQL из этого ответа:

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)
Адриан Карр
источник