Bir veritabanına yapılan tüm bağlantıları öldüren komut dosyası (RESTRICTED_USER ROLLBACK'ten daha fazlası)


239

Bir Visual Studio Veritabanı projesinden (TFS Auto Build aracılığıyla) sık sık yeniden dağıtan bir geliştirme veritabanı var.

Bazen derlememi çalıştırdığımda şu hatayı alıyorum:

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.  

Bunu denedim:

ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE

ama yine de veritabanını bırakamıyorum. (Sanırım geliştiricilerin çoğunun dboerişimi var.)

Elle çalıştırıp SP_WHObağlantıları öldürmeye başlayabilirim, ancak bunu otomatik derlemede yapmak için otomatik bir yola ihtiyacım var. (Bu sefer bağlantım db üzerindeki tek kişi olsa da bırakmaya çalışıyorum.)

Kimin bağlı olduğuna bakılmaksızın veritabanımı bırakabilecek bir komut dosyası var mı?

Yanıtlar:


642

Güncellenmiş

MS SQL Server 2012 ve üstü için

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 için

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

25
Bu ikisinin daha iyi cevabı; veritabanını çevrimdışına almaktan kaçınır ve kabul edilen cevap her zaman işe yaramaz (bazen her şeyi geri alamaz).
Mark Henderson

3
killİfadeleri bir araya toplamak için böyle güzel bir cevap . Tabii ki hiç verimli olmayan her işlemi öldürmek için bir imleç kullanırdım. Bu cevapta kullanılan teknik mükemmel.
Saeed Neamati

Mark'a katılıyorum. Bu yöntem, veritabanları için çok daha zarif ve daha az etkili olduğu için kabul edilen cevap olmalıdır.
Austin S.

3
iyi ve hızlı. Tek sorun sistem spid olabilir, bu yüzden u nereye WHERE ekleyebilirsiniz dbid = db_id ('My_db') ve spid> 50
Saurabh Sinha

1
@FrenkyB Komut dosyasını çalıştırmadan önce veritabanı içeriğini değiştirmeniz gerekir. Örneğin:USE [Master]
AlexK

133
USE master
GO
ALTER DATABASE database_name
SET OFFLINE WITH ROLLBACK IMMEDIATE
GO

Ref: http://msdn.microsoft.com/tr-tr/library/bb522682%28v=sql.105%29.aspx


9
İşin garibi USE master, anahtar buydu. Ben bağlı iken db düşürmeye çalışıyordu (Duh!). Teşekkürler!
Vaccano

9
Kullanıyorsanız SET OFFLINE, db dosyalarını el ile silmeniz gerekir.
mattalxndr

5
Daha alter database YourDatabaseName set SINGLE_USER with rollback immediateiyi olmaz mıydı ? Eğer bunu ayarlarsanız OFFLINE(@mattalxndr devletler olarak) dosyaları diskte, ama sol edilecektir SINGLE_USERbağlantınız tek olarak kalacak ve drop database YourDatabaseNameyine dosya kaldıracaktır.
Keith

1
@Komut dosyasında DB'ye bağlı değilsiniz, bu yüzden "bağlantınız" değil, başka bir şey kalmayacak. Hemen sonra set offline, sen verebilir set online(evet, bir yarış durumu olasılık var) artık dosyaları sorunu önlemek için.
ivan_pozdeev

2
Teşekkürler! Bu veritabanında daha önce yürütülen SQL Management Studio sql deyimi ile bazı sekme benim db kullanımda rapor neden olduğunu fark etmedi. Usta kullanın ve gidin, her şeyi işe yaradı!
eythort

26

SSMS'nin sağladığı komut dosyasını aşağıdakileri yaparak alabilirsiniz:

  1. SSMS'de bir veritabanını sağ tıklayın ve sil'i seçin
  2. İletişim kutusunda, "Mevcut bağlantıları kapat" onay kutusunu işaretleyin.
  3. İletişim kutusunun üst kısmındaki Komut Dosyası düğmesini tıklayın.

Komut dosyası şöyle görünecektir:

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

3
Bu bazı uygulama kullanıcılarına gevşek akım bağlantısı neden olabilir ve bu kullanıcıları bulmak ve aynı veya birkaç kez öldürmek için tek kullanıcı modunda veritabanını almayı tavsiye etmeyeceğim u db bağlantıları varsa sql sunucusunu yeniden başlatmanız gerekir çok sık.
Saurabh Sinha

7

Az bilinen: GO sql deyimi, önceki komutu tekrarlamak için tamsayı alabilir.

Yani eğer:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

Sonra:

USE [DATABASENAME]
GO 2000

Bu, USE komutunu 2000 kez tekrarlar, diğer tüm bağlantılarda kilitlenmeyi zorlar ve tek bağlantının sahipliğini alır. (İstediğiniz gibi sorgu penceresine tek erişim izni verme.)


2
GO bir TSQL komutu değil, yalnızca sqlcmd ve osql yardımcı programları ve SSMS tarafından tanınan özel bir komuttur.
diceless

4

Deneyimlerime göre, SINGLE_USER kullanmak çoğu zaman yardımcı olur, ancak dikkatli olunmalıdır: SINGLE_USER komutunu başlattığım zamanla bittiği zaman arasında ... görünüşe göre başka bir 'kullanıcı' SINGLE_USER erişimi, ben değil. Böyle bir durumda, veritabanına erişimi geri almaya çalışan zor bir iş içindesiniz (benim durumumda, benden önce SINGLE_USER erişimini elinde tutan SQL veritabanlarına sahip bir yazılım için çalışan belirli bir hizmetti). Bence en güvenilir yol olmalı (bunun için kefil olamaz, ama önümüzdeki günlerde test edeceğim şey), aslında:
- erişiminizi engelleyebilecek hizmetleri durdurun (varsa)
- tüm bağlantıları kapatmak için yukarıdaki 'kill' komut dosyasını kullanın
- hemen ardından veritabanını single_user olarak ayarlayın
- sonra geri yüklemeyi yapın


SINGLE_USER komutu (komut dosyası) geri yükleme komutunuzla aynı toplu işteyse - bir GO ifadesi ile ayrılmaz! - o zaman başka hiçbir süreç benim deneyimime göre tek kullanıcı erişiminden yararlanamaz. Ancak, bu gece yakalandım çünkü gece planlı set-tek kullanıcılı işim; geri yükleme; set-çok kullanıcılı havaya uçtu. Başka bir işlem benim bak dosyama (smh) özel dosya erişimi vardı ve bu nedenle geri yükleme başarısız oldu, ardından SET MULTI_USER başarısız oldu ... yani gece kanı temizlemek için çağrıldığında, başka birinin SINGLE_USER erişimi vardı ve öldürülmesi gerekiyordu.
Ross Presser

3

Matthew'un son derece etkili betiği, kullanımdan kaldırılmış sysprocesses sistem tablosunun yerini alarak dm_exec_sessions DMV'yi kullanacak şekilde güncellendi:

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 döngüsünü kullanarak alternatif (yürütme başına başka işlemleri işlemek istiyorsanız):

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;

2

Kabul edilen cevap, bir veritabanının bağlı olandan başka bir veritabanındaki tabloları içeren bir sorguyu yürüten bir bağlantı tarafından kilitlenebileceğini dikkate alma dezavantajına sahiptir.

Sunucu örneğinin birden fazla veritabanı varsa ve sorgu doğrudan veya dolaylı olarak (örneğin eşanlamlılar aracılığıyla) birden fazla veritabanında tablolar vb. Kullanıyorsa, bu durum söz konusu olabilir.

Bu nedenle öldürmek için bağlantıları bulmak için bazen syslockinfo kullanmanın daha iyi olduğunu düşünüyorum.

Bu nedenle önerim AlexK tarafından kabul edilen cevabın aşağıdaki varyasyonunu kullanmak olacaktır:

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

BU! Ben şahsen eski işaretli sys.dm_tran_locksolarak tabloyu kullanmak rağmen syslockinfo, Ayrıca, ne olursa olsun mevcut @@ SPID hariç tutmak isteyebilirsiniz.
deroby

1

Öldürme işlemleri sırasında istisnalar konusunda dikkatli olmalısınız. Yani bu betiği kullanabilirsiniz:

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

@AlexK harika bir cevap yazdı . Sadece iki sentimi eklemek istiyorum. Aşağıdaki kod tamamen @ AlexK'nin cevabına dayanmaktadır, fark, kullanıcıyı ve son toplu işin yürütülmesinden bu yana bir süreyi belirtebilirsiniz (kodun master..sysprocess yerine sys.dm_exec_sessions kullandığını unutmayın):

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)

Bu örnekte, yalnızca son toplu işin 1 saatten daha önce gerçekleştirildiği usrDBTest kullanıcısının işlemi öldürülecektir.


1

Sen kullanabilirsiniz İmleç böyle:

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

Bunun hakkında blogumda yazdım: http://www.pigeonsql.com/single-post/2016/12/13/Kill-all-connections-on-DB-by-Cursor


0
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
*/

-1

Aşağıdaki basit kod ile başarılı bir şekilde test ettim

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

2
set SINGLE_USERTek bir etkin bağlantının olduğu zamanlarda sorun yaşadım .
ivan_pozdeev
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.