SQL Server Tidak Dapat menjatuhkan basis data <dbname> karena sedang digunakan ... tetapi tidak ada sesi yang ditampilkan

72

Ketika saya mencoba untuk menjatuhkan database saya mendapatkan kesalahan "Tidak bisa drop database" dbname "karena saat ini sedang digunakan". Namun, ketika saya menjalankan sp_who2, pasti tidak ada sesi yang terhubung ke database ini. Saya juga telah mengatur basis data single_user mode with rollback immediate.

Mengapa ini terjadi?

tuseau
sumber

Jawaban:

20

Pastikan Anda tidak memiliki dependensi seperti snapshot basis data pada db yang ingin Anda hapus. Padahal, pesan kesalahan akan terlihat sebaliknya. Apakah Anda yakin tidak ada proses tersembunyi yang menghubungkan ke database Anda? Pendekatan yang baik adalah dengan menjalankan skrip yang membunuh semua sesi dan segera setelah mengubah nama database menjadi nama lain dan kemudian drop database.

buat kursor berdasarkan pada pilih ini:

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

masalah di dalam kursor:

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

setelah kursor ditutup dan tidak dialokasikan:

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

go

sp_renamedb 'my_db', 'my_db_old'

go

DROP DATABASE MY_DB_OLD 
yrushka
sumber
Terima kasih untuk kodenya - itu mungkin berhasil. Yang tidak saya mengerti adalah, apa itu sesi "tersembunyi"? Saya akan berpikir sp_who dan metadata lainnya (DMVs) akan menunjukkan semua sesi, selain itu apa gunanya mereka?
tuseau
Ya biasanya Anda harus dapat melihat semua aktif / tidak aktif melalui sp_who atau permintaan tabel sysprocesses dari master db. Secara tersembunyi maksud saya adalah proses yang menghubungkan kembali dari layanan aplikasi. Tepuk tangan.
yrushka
1
Ini kuno karena beberapa alasan: (1) gaya lama bergabung (2) tampilan kompatibilitas mundur (3) kursor dan SQL dinamis untuk menjalankan banyak perintah KILL ketika ALTER tunggal akan melakukan (4) prosedur usang seperti sp_dboption.
Aaron Bertrand
1
Sayangnya saya tidak berpikir ini menjawab pertanyaan - penanya bertanya mengapa ini terjadi, bukan bagaimana menyelesaikannya. Jawaban yang diberikan berhasil, tetapi saya masih tidak tahu apa yang membuat saya tidak menghapus database. @AaronBertrand menyebutkan "bahkan Object Explorer bisa menjadi pelakunya" yang sebenarnya menjadi alasan SATU dari database, tetapi bagaimana saya bisa tahu itu adalah Object Explorer pasti?
LearnByReading
ini memberi saya kesalahan "Tidak dapat menggunakan KILL untuk membunuh proses Anda sendiri"
nuander
80

Sesi yang terhubung ke basis data lain mungkin memiliki transaksi terbuka yang juga memengaruhi basis data Anda - sp_who2 hanya akan menampilkan satu basis data. Itu bisa juga sesuatu yang sederhana seperti Obyek Explorer atau Rincian Obyek Explorer terbuka di SSMS, yang lagi-lagi hanya akan menampilkan satu database di sp_who2.

Jangan repot-repot mencari sesi yang bertanggung jawab; bunuh saja semuanya dengan satu pernyataan (dan pastikan itu bukan salinan SSMS Anda yang terhubung, mis. jendela kueri lain, Object Explorer, dll.):

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

Sekarang Anda dapat menjatuhkannya, dan melakukannya menggunakan DDL, bukan UI:

DROP DATABASE dbname;
Aaron Bertrand
sumber
1
Terima kasih atas jawaban Anda, ini berhasil. Tapi saya hanya mengalami kesulitan untuk hidup dengan solusi ini: mengapa saya tidak bisa meninggalkan beberapa database karena kesalahan ini? Saya memiliki beberapa database yang belum tersentuh selama satu tahun dan tidak ada proses atau transaksi nyata yang terhubung dengannya. Bisakah Anda memberi saya beberapa petunjuk untuk membantu saya menemukan layanan potensial, atau transaksi atau apa pun yang terhubung ke database ini?
LearnByReading
1
Sebenarnya, yang harus saya lakukan adalah USE master, kalau begitu DROP DATABASE dbname. Rupanya yang dibutuhkan hanyalah "menggunakan" sesuatu yang lain, untuk melepaskan db.
vapcguy
2
@vapcguy Itu hanya benar jika jendela permintaan Anda saat ini adalah satu-satunya koneksi. Ini biasanya tidak terjadi (dan itulah sebabnya jawaban saya menyatakan "dan pastikan itu bukan salinan SSMS Anda yang terhubung").
Aaron Bertrand
20

Apa basis data Anda saat ini ketika Anda mengeluarkan DROPperintah? Coba ini:

use master
go
drop database mydb
go

Pastikan juga Anda terhubung sebagai sadan bukan dbopada basis data apa pun yang ingin Anda jatuhkan.

Gayus
sumber
Saya pasti terhubung dengan master. Saya tidak harus terhubung sebagai sa untuk menjatuhkan database. Bagi saya ini seperti bug - ini tidak menampilkan sesi, atau berpikir ada sesi yang digunakan tetapi tidak ada.
tuseau
3
Saya baru saja terjebak dengan ini - mencoba menjalankan drop script dengan konteks yang ditetapkan ke database dari sqlcmd prompt! Doh
JonnyRaa
18

Bagaimana kalau hanya melihat apa yang dilakukan SSMS saat Anda menggunakan UI tetapi suruh untuk mengeluarkan skrip untuk tindakan? Inilah yang dilakukan SSMS ketika Anda mengklik kanan DB dan memilih Hapus, lalu centang kotak untuk menutup koneksi yang ada:

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
sumber
... dengan asumsi, tentu saja, tidak apa-apa untuk
mengembalikan
4
Anda menjatuhkan database, saya akan menganggap itu cukup baik.
georgiosd
1
Ini berhasil untuk saya! :)
Leonardo Trimarchi
5

Saya telah menghadapi situasi ini berkali-kali dan di bawah ini adalah apa yang saya lakukan:

Ketika metode yang jelas tidak berhasil ..... (seperti dalam situasi Anda):

Cari tahu ID basis data dari sysdatabases.

Kemudian jalankan - sp_lockyang akan menunjukkan semua kunci pada instance bersama dengan spid dan dbid.

Bunuh spid dengan dbid yang sedang Anda coba offline atau jatuhkan.

Padahal, prosesnya agak manual, bisa otomatis seperti di bawah ini:

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

Menemukan jawaban yang sangat sederhana di StackOverflow yang berfungsi pertama kali untuk saya:

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

Inilah SQL dari jawaban itu:

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
sumber