Jak określić „zamknij istniejące połączenia” w skrypcie sql

153

Aktywnie rozwijam mój schemat w SQL Server 2008 i często chcę ponownie uruchamiać mój skrypt upuszczania / tworzenia bazy danych. Kiedy biegnę

USE [master]
GO

IF  EXISTS (SELECT name FROM sys.databases WHERE name = N'MyDatabase')
DROP DATABASE [MyDatabase]
GO

Często otrzymuję ten błąd

Msg 3702, Level 16, State 4, Line 3
Cannot drop database "MyDatabase" because it is currently in use.

Jeśli klikniesz prawym przyciskiem myszy bazę danych w panelu eksploratora obiektów i wybierzesz zadanie Usuń z menu kontekstowego, pojawi się pole wyboru umożliwiające "zamknięcie istniejących połączeń"

Czy istnieje sposób na określenie tej opcji w moim skrypcie?

nacięcie
źródło

Odpowiedzi:

247

Możesz rozłączyć wszystkich i cofnąć ich transakcje za pomocą:

alter database [MyDatbase] set single_user with rollback immediate

Następnie możesz bezpiecznie usunąć bazę danych :)

Andomar
źródło
8
Skorzystałem z tego, ale często zastanawiałem się, czy inny użytkownik miałby okazję wejść jako „pojedynczy użytkownik” - czy to możliwe? Możliwą alternatywą jest ALTER DATABASE [MyDatabaseName] USTAWIENIE OFFLINE Z NATYCHMIASTOWYM ROLLBACK
Kristen
9
Użytkownik w single_user to Ty; chyba że rozłączysz się po ustawieniu trybu pojedynczego użytkownika. Wtedy jeden (1) inny użytkownik może się zalogować.
Andomar
Po upuszczeniu bazy danych, jeśli utworzysz nową o tej samej nazwie, zakładam, że będzie ona w trybie wielu użytkowników? Więc nie musisz uruchamiać: zmień bazę danych [MyDatbase] ustaw multi_user
AndyM
@AndyM: Tak, opcja multi_user jest prawdopodobnie domyślna
Andomar
2
@Kristen Korzystając z twojego podejścia stwierdziłem, że serwer sql nie usuwa plików mdf i ldf. Dla mnie ustawienie single_user działa dobrze (muszę stale odtwarzać bazę danych).
2xMax
36

Idź do studia zarządzania i zrób wszystko, co opisujesz, tylko zamiast kliknąć OK, kliknij Skrypt. Pokaże kod, który uruchomi, który możesz następnie włączyć do swoich skryptów.

W takim przypadku chcesz:

ALTER DATABASE [MyDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
hgmnz
źródło
1
Zamierzałem odpowiedzieć na to pytanie, robiąc dokładnie to, co opisujesz (tworzenie skryptów w oknie dialogowym „Usuń bazę danych”), ale nie dodaje to wiersza ALTER DATABASE do skryptu, jeśli zaznaczysz pole wyboru „zamknij istniejące połączenia”.
Matt Hamilton,
Skrypt generowany z kreatora zawierał dla mnie wiersz „alter database”.
nick
Dziwne. Która wersja Management Studio? Jestem na 2008 x64.
Matt Hamilton,
Ja też: Microsoft SQL Server Management Studio 10.0.1600.22 System operacyjny 6.0.6001
nick
8
Miałem ten sam problem z brakiem dodania ALTER DATABASE do skryptu. Aby móc dodać go do skryptu, musiałem upewnić się, że mam uruchomiony proces (aktywne połączenie) z tą bazą danych podczas generowania skryptu.
Gilbert,
16

Zgodnie z dokumentacją ALTER DATABASE SET , nadal istnieje możliwość, że po ustawieniu bazy danych w trybie SINGLE_USER nie będziesz mieć dostępu do tej bazy danych:

Przed ustawieniem bazy danych na SINGLE_USER, sprawdź, czy opcja AUTO_UPDATE_STATISTICS_ASYNC jest ustawiona na OFF. Gdy opcja jest włączona, wątek działający w tle używany do aktualizacji statystyk pobiera połączenie z bazą danych i nie będzie można uzyskać dostępu do bazy danych w trybie pojedynczego użytkownika.

Tak więc kompletny skrypt usuwający bazę danych z istniejącymi połączeniami może wyglądać następująco:

DECLARE @dbId int
DECLARE @isStatAsyncOn bit
DECLARE @jobId int
DECLARE @sqlString nvarchar(500)

SELECT @dbId = database_id,
       @isStatAsyncOn = is_auto_update_stats_async_on
FROM sys.databases
WHERE name = 'db_name'

IF @isStatAsyncOn = 1
BEGIN
    ALTER DATABASE [db_name] SET  AUTO_UPDATE_STATISTICS_ASYNC OFF

    -- kill running jobs
    DECLARE jobsCursor CURSOR FOR
    SELECT job_id
    FROM sys.dm_exec_background_job_queue
    WHERE database_id = @dbId

    OPEN jobsCursor

    FETCH NEXT FROM jobsCursor INTO @jobId
    WHILE @@FETCH_STATUS = 0
    BEGIN
        set @sqlString = 'KILL STATS JOB ' + STR(@jobId)
        EXECUTE sp_executesql @sqlString
        FETCH NEXT FROM jobsCursor INTO @jobId
    END

    CLOSE jobsCursor
    DEALLOCATE jobsCursor
END

ALTER DATABASE [db_name] SET  SINGLE_USER WITH ROLLBACK IMMEDIATE

DROP DATABASE [db_name]
AlexD
źródło
3

Wiem, że jest za późno, ale może to komuś pomóc. korzystając z tego, przenieś bazę danych do trybu offline

ALTER DATABASE dbname SET OFFLINE
Abhishek Upadhyay
źródło
uwaga: po "upuszczeniu bazy danych offline" plik Mdf nie został usunięty! stackoverflow.com/questions/33154141/…
bob217
2

Wypróbowałem to, co powiedział hgmnz na SQL Server 2012.

Kierownictwo stworzone dla mnie:

EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'MyDataBase'
GO
USE [master]
GO
/****** Object:  Database [MyDataBase]    Script Date: 09/09/2014 15:58:46 ******/
DROP DATABASE [MyDataBase]
GO
Deiwys
źródło
4
Nie spowoduje to zamknięcia aktywnych połączeń.
Jens
Upewnij się, że zaznaczyłeś "Zamknij istniejące połączenia"; Jeśli nie zrobisz, ROLLBACK IMMEDIATEoświadczenie zostanie dołączone. sp_delete_database_backuphistoryPochodzi od sprawdzania „Usuń kopii zapasowej i przywracania baz danych informacji o historii”.
Christian.K
Zaznaczenie opcji „Zamknij istniejące połączenia” nie powoduje wygenerowania komunikatu, ALTER DATABASE SET SINGLE_USER ...jeśli nie ma bieżących połączeń do zamknięcia.
ahwm
-1

wypróbuj ten kod C #, aby porzucić bazę danych

public static void DropDatabases (string dataBase) {

        string sql =  "ALTER DATABASE "  + dataBase + "SET SINGLE_USER WITH ROLLBACK IMMEDIATE" ;

        using (System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings["DBRestore"].ConnectionString))
        {
            connection.Open();
            using (System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(sql, connection))
            {
                command.CommandType = CommandType.Text;
                command.CommandTimeout = 7200;
                command.ExecuteNonQuery();
            }
            sql = "DROP DATABASE " + dataBase;
            using (System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(sql, connection))
            {
                command.CommandType = CommandType.Text;
                command.CommandTimeout = 7200;
                command.ExecuteNonQuery();
            }
        }
    }
Shailesh Tiwari
źródło