it-swarm.asia

Script untuk mematikan semua koneksi ke database (Lebih dari RESTRICTED_USER ROLLBACK)

Saya memiliki database pengembangan yang sering digunakan kembali dari proyek Database Visual Studio (melalui TFS Auto Build).

Terkadang ketika saya menjalankan build saya, saya mendapatkan kesalahan ini:

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.  

Saya mencoba ini:

ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE

tapi saya masih tidak bisa drop database. (Dugaan saya adalah bahwa sebagian besar pengembang memiliki akses dbo.)

Saya dapat menjalankan SP_WHO secara manual dan mulai mematikan koneksi, tetapi saya perlu cara otomatis untuk melakukan ini dalam pembuatan otomatis. (Meskipun kali ini koneksi saya adalah satu-satunya di db yang saya coba jatuhkan.)

Apakah ada skrip yang dapat menjatuhkan basis data saya terlepas dari siapa yang terhubung?

189
Vaccano

Diperbarui

Untuk MS SQL Server 2012 dan di atasnya

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

Untuk 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); 
539
AlexK
USE master
GO
ALTER DATABASE database_name
SET OFFLINE WITH ROLLBACK IMMEDIATE
GO

Ref: http://msdn.Microsoft.com/en-us/library/bb522682%28v=sql.105%29.aspx

123
Chains

Anda bisa mendapatkan skrip yang disediakan SSMS dengan melakukan hal berikut:

  1. Klik kanan pada database di SSMS dan pilih delete
  2. Dalam dialog, centang kotak centang untuk "Tutup koneksi yang ada."
  3. Klik tombol Script di bagian atas dialog.

Script akan terlihat seperti ini:

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

Sedikit diketahui: pernyataan GO sql dapat mengambil bilangan bulat untuk berapa kali mengulangi perintah sebelumnya.

Jadi jika kamu:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

Kemudian:

USE [DATABASENAME]
GO 2000

Ini akan mengulangi perintah USE 2000 kali, memaksakan kebuntuan pada semua koneksi lain, dan mengambil kepemilikan koneksi tunggal. (Memberikan akses tunggal jendela permintaan Anda untuk melakukan apa yang Anda inginkan.)

6
Sodoshi

Skrip Matthew yang sangat efisien diperbarui untuk menggunakan DMV dm_exec_sessions, menggantikan tabel sistem sysprocess yang sudah usang:

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;

Alternatif menggunakan loop WHILE (jika Anda ingin memproses operasi lain per eksekusi):

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;
3
Chris Bates

Untuk pengalaman saya, menggunakan SINGLE_USER membantu sebagian besar waktu, bagaimanapun, kita harus berhati-hati: Saya pernah mengalami saat-saat di mana antara saat saya memulai perintah SINGLE_USER dan waktu selesai ... tampaknya 'pengguna' lain telah mendapatkan SINGLE_USER akses, bukan saya. Jika itu terjadi, Anda berada dalam pekerjaan berat yang berusaha mendapatkan akses ke database kembali (dalam kasus saya, itu adalah layanan khusus yang berjalan untuk perangkat lunak dengan database SQL yang mendapatkan akses SINGLE_USER sebelum saya melakukannya). Apa yang saya pikir harus menjadi cara yang paling dapat diandalkan (tidak dapat menjaminnya, tetapi itu adalah apa yang akan saya uji di masa mendatang), sebenarnya:
- hentikan layanan yang dapat mengganggu akses Anda (jika ada)
- gunakan skrip 'kill' di atas untuk menutup semua koneksi
- atur database menjadi single_user segera setelah itu
- lalu lakukan pengembalian

3
Sacha

Anda dapat menggunakan Kursor seperti itu:

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

Saya menulis tentang itu di blog saya di sini: http://www.pigeonsql.com/single-post/2016/12/13/Kill-all-connections-on-DB-by-Cursor

1
Filip Holub

Anda harus berhati-hati tentang pengecualian selama proses pembunuhan. Jadi, Anda dapat menggunakan skrip ini:

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
Shahriar Khazaei

Jawaban yang diterima memiliki kelemahan yang tidak mempertimbangkan bahwa database dapat dikunci oleh koneksi yang mengeksekusi kueri yang melibatkan tabel dalam database selain dari yang terhubung.

Ini dapat menjadi kasus jika instance server memiliki lebih dari satu database dan permintaan secara langsung atau tidak langsung (misalnya melalui sinonim) menggunakan tabel di lebih dari satu database dll.

Karena itu saya menemukan bahwa kadang-kadang lebih baik menggunakan syslockinfo untuk menemukan koneksi untuk mematikan.

Karenanya saran saya adalah menggunakan variasi jawaban yang diterima dari AlexK di bawah ini:

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

@AlexK menulis jawaban hebat. Saya hanya ingin menambahkan dua sen saya. Kode di bawah ini sepenuhnya didasarkan pada jawaban @ AlexK, perbedaannya adalah Anda dapat menentukan pengguna dan waktu sejak batch terakhir dijalankan (perhatikan bahwa kode tersebut menggunakan sys.dm_exec_sessions alih-alih 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)

Dalam contoh ini hanya proses pengguna usrDBTest yang batch terakhir dieksekusi lebih dari 1 jam yang lalu akan dibunuh.

1
cantoni
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
*/
0
Emrah Saglam