Mam programistyczną bazę danych, która często wdraża się ponownie z projektu Visual Studio Database (poprzez TFS Auto Build).
Czasami po uruchomieniu kompilacji pojawia się ten błąd:
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.
Próbowałem tego:
ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE
ale nadal nie mogę upuścić bazy danych. (Domyślam się, że większość programistów ma dbo
dostęp.)
Mogę ręcznie uruchomić SP_WHO
i zacząć zabijać połączenia, ale potrzebuję automatycznego sposobu, aby to zrobić w automatycznej kompilacji. (Chociaż tym razem moje połączenie jest jedyne w bazie danych, którą próbuję usunąć).
Czy istnieje skrypt, który może upuścić moją bazę danych niezależnie od tego, kto jest podłączony?
kill
instrukcji razem. Użyłbym kursora, aby zabić każdy proces, co oczywiście nie jest wcale wydajne. Technika zastosowana w tej odpowiedzi jest genialna.USE [Master]
Ref: http://msdn.microsoft.com/en-us/library/bb522682%28v=sql.105%29.aspx
źródło
USE master
to był klucz. Próbowałem usunąć db podczas połączenia z nim (Duh!). Dzięki!SET OFFLINE
, musisz ręcznie usunąć pliki db.alter database YourDatabaseName set SINGLE_USER with rollback immediate
byłoby lepiej? Jeśli ustawisz naOFFLINE
(jak stwierdza @mattalxndr), pliki pozostaną na dysku, ale przySINGLE_USER
twoim połączeniu pozostanie jako jedyny idrop database YourDatabaseName
nadal usunie pliki.set offline
możesz wydać,set online
aby uniknąć problemu z pozostałymi plikami (tak, istnieje możliwość wystąpienia wyścigu).Możesz uzyskać skrypt dostarczany przez SSMS, wykonując następujące czynności:
Skrypt będzie wyglądał mniej więcej tak:
źródło
Mało znane: instrukcja GO sql może przyjmować liczbę całkowitą powtarzającą poprzednie polecenie.
Więc jeśli ty:
Następnie:
Spowoduje to powtórzenie polecenia USE 2000 razy, wymuszenie zakleszczenia na wszystkich innych połączeniach i przejęcie na własność pojedynczego połączenia. (Dając dostęp do okna zapytania, możesz robić to, co chcesz.)
źródło
Z mojego doświadczenia wynika, że używanie SINGLE_USER pomaga w większości przypadków, jednak należy zachować ostrożność: zdarzały mi się sytuacje, w których od momentu uruchomienia polecenia SINGLE_USER do momentu jego zakończenia ... najwyraźniej inny „użytkownik” otrzymał Dostęp SINGLE_USER, nie ja. Jeśli tak się stanie, czeka cię ciężka praca, próbująca odzyskać dostęp do bazy danych (w moim przypadku była to konkretna usługa działająca dla oprogramowania z bazami SQL, które wcześniej uzyskało dostęp do SINGLE_USER). To, co moim zdaniem powinno być najbardziej niezawodnym sposobem (nie mogę za to ręczyć, ale to właśnie przetestuję w nadchodzących dniach), to w rzeczywistości:
- zatrzymaj usługi, które mogą zakłócać twój dostęp (jeśli takie istnieją)
- użyj powyższego skryptu „kill”, aby zamknąć wszystkie połączenia
- natychmiast po tym ustaw bazę danych na single_user
- następnie przywróć
źródło
Niezwykle wydajny skrypt Matthew został zaktualizowany, aby używać DMV dm_exec_sessions, zastępując przestarzałą tabelę systemową sysprocesses:
Alternatywnie przy użyciu pętli WHILE (jeśli chcesz przetwarzać inne operacje na wykonanie):
źródło
Przyjęta odpowiedź ma tę wadę, że nie bierze pod uwagę, że bazę danych można zablokować przez połączenie, które wykonuje zapytanie obejmujące tabele w bazie danych innej niż ta, z którą jest połączone.
Może się tak zdarzyć, jeśli instancja serwera ma więcej niż jedną bazę danych, a zapytanie bezpośrednio lub pośrednio (na przykład przez synonimy) korzysta z tabel w więcej niż jednej bazie danych itp.
Dlatego uważam, że czasem lepiej jest użyć syslockinfo do znalezienia połączeń do zabicia.
Sugeruję zatem użycie poniższej odmiany zaakceptowanej odpowiedzi od AlexK:
źródło
sys.dm_tran_locks
tabeli jakosyslockinfo
oznaczonej jako przestarzała, możesz również na wszelki wypadek wykluczyć bieżący identyfikator @@ SPID.Powinieneś uważać na wyjątki podczas procesów zabijania. Możesz więc użyć tego skryptu:
źródło
@AlexK napisał świetną odpowiedź . Chcę tylko dodać moje dwa centy. Poniższy kod jest całkowicie oparty na odpowiedzi @ AlexK, różnica polega na tym, że możesz określić użytkownika i czas od ostatniego uruchomienia partii (zwróć uwagę, że kod używa sys.dm_exec_sessions zamiast master..sysprocess):
W tym przykładzie zabity zostanie tylko proces użytkownika usrDBTest, którego ostatnia partia została wykonana ponad godzinę temu.
źródło
Możesz użyć takiego kursora :
Napisałem o tym na moim blogu tutaj: http://www.pigeonsql.com/single-post/2016/12/13/Kill-all-connections-on-DB-by-Cursor
źródło
źródło
Testowałem pomyślnie z prostym kodem poniżej
źródło
set SINGLE_USER
kiedy było już jedno aktywne połączenie.