Script, чтобы убить все подключения к базе данных (Больше, чем RESTRICTED_USER ROLLBACK)
У меня есть база данных разработки, которая часто переустанавливается из проекта базы данных Visual Studio (через TFS Auto Build).
Иногда, когда я запускаю свою сборку, я получаю эту ошибку:
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.
Я попробовал это:
ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE
но я все еще не могу удалить базу данных. (Я предполагаю, что большинство разработчиков имеют доступ к dbo
.)
Я могу вручную запустить SP_WHO
и начать SP_WHO
соединения, но мне нужен автоматический способ сделать это в автоматической сборке. (Хотя на этот раз мое соединение - единственное в БД, которое я пытаюсь разорвать.)
Есть ли скрипт, который может отбросить мою базу данных независимо от того, кто подключен?
Ответы
Ответ 1
Обновление
Для MS SQL Server 2012 и выше
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);
Для 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);
Ответ 2
USE master
GO
ALTER DATABASE database_name
SET OFFLINE WITH ROLLBACK IMMEDIATE
GO
Ссылка: http://msdn.microsoft.com/en-us/library/bb522682%28v=sql.105%29.aspx
Ответ 3
Вы можете получить script, который предоставляет SSMS, выполнив следующие действия:
- Щелкните правой кнопкой мыши по базе данных в SSMS и выберите delete
- В диалоговом окне установите флажок "Закрыть существующие подключения".
- Нажмите кнопку script в верхней части диалогового окна.
script будет выглядеть примерно так:
USE [master]
GO
ALTER DATABASE [YourDatabaseName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
USE [master]
GO
DROP DATABASE [YourDatabaseName]
GO
Ответ 4
Мало известно: оператор GO sql может принимать целое число за количество повторений предыдущей команды.
Итак, если вы:
ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO
Тогда:
USE [DATABASENAME]
GO 2000
Это повторит команду USE 2000 раз, заставит тупик на всех других подключениях и получит право собственности на одно соединение. (Предоставление единственного доступа к окну запроса для выполнения, как вы пожелаете.)
Ответ 5
По моему опыту, использование SINGLE_USER в большинстве случаев помогает, однако, следует быть осторожным: у меня были случаи, когда между временем, когда я запускаю команду SINGLE_USER, и временем, когда она закончена... очевидно, другой "пользователь" получил SINGLE_USER доступ, а не я. Если это произойдет, вам придется нелегко пытаться вернуть доступ к базе данных (в моем случае это была специальная служба, работающая для программного обеспечения с базами данных SQL, которая получила доступ SINGLE_USER раньше, чем я). То, что я думаю, должно быть самым надежным способом (не могу ручаться за это, но это то, что я проверю в ближайшие дни), на самом деле:
- прекратить услуги, которые могут помешать вашему доступу (если есть)
- используйте скрипт "kill" выше, чтобы закрыть все соединения
- установить базу данных на single_user сразу после этого
- тогда сделай восстановление
Ответ 6
Мэтью в высшей степени эффективный script обновлен, чтобы использовать DMV dm_exec_sessions, заменив таблицу систем устаревшей системы 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;
Альтернатива с использованием цикла WHILE (если вы хотите обработать любые другие операции за выполнение):
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;
Ответ 7
Вы должны быть осторожны в отношении исключений во время процессов убийства. Таким образом, вы можете использовать этот 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)
Ответ 8
@AlexK написал отличный ответ. Я просто хочу добавить свои два цента. Код ниже полностью основан на ответе @AlexK, разница в том, что вы можете указать пользователя и время с момента выполнения последней партии (обратите внимание, что код использует sys.dm_exec_sessions вместо 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)
В этом примере будет убит только процесс пользователя usrDBTest, который был выполнен последней партией более 1 часа назад.
Ответ 9
Вы можете использовать Курсор:
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
Я писал об этом в своем блоге:
http://www.pigeonsql.com/single-post/2016/12/13/Kill-all-connections-on-DB-by-Cursor
Ответ 10
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
*/
Ответ 11
Принятый ответ имеет недостаток, заключающийся в том, что он не принимает во внимание, что база данных может быть заблокирована соединением, которое выполняет запрос, который включает таблицы в базе данных, отличной от той, к которой подключен.
Это может быть в том случае, если экземпляр сервера имеет более одной базы данных, а запрос прямо или косвенно (например, через синонимы) использует таблицы в более чем одной базе данных и т.д.
Поэтому я считаю, что иногда лучше использовать syslockinfo, чтобы найти соединения для уничтожения.
Поэтому я предложил бы использовать приведенный ниже вариант принятого ответа от 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);
Ответ 12
Я успешно тестировал простой код ниже
USE [master]
GO
ALTER DATABASE [YourDatabaseName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO