डेटाबेस से सभी कनेक्शनों को मारने के लिए स्क्रिप्ट (RESTRICTED_USER ROLLBACK से अधिक)


239

मेरे पास एक विकास डेटाबेस है जो विज़ुअल स्टूडियो डेटाबेस प्रोजेक्ट (टीएफएस ऑटो बिल्ड के माध्यम से) से अक्सर तैनात होता है।

कभी-कभी जब मैं अपना निर्माण चलाता हूं तो मुझे यह त्रुटि मिलती है:

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.  

मैंने यह कोशिश की:

ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE

लेकिन मैं अभी भी डेटाबेस को नहीं छोड़ सकता। (मेरा अनुमान है कि अधिकांश डेवलपर्स की dboपहुंच है।)

मैं मैन्युअल रूप से चला सकता हूं SP_WHOऔर कनेक्शन को मारना शुरू कर सकता हूं , लेकिन मुझे ऑटो बिल्ड में ऐसा करने के लिए एक स्वचालित तरीका चाहिए। (हालांकि इस बार मेरा कनेक्शन केवल उसी db पर है जिसे मैं छोड़ने की कोशिश कर रहा हूं।)

क्या कोई स्क्रिप्ट है जो कनेक्ट किए बिना मेरे डेटाबेस को छोड़ सकती है?

जवाबों:


642

अपडेट किया गया

एमएस SQL ​​सर्वर 2012 और इसके बाद के संस्करण के लिए

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

एमएस SQL ​​सर्वर 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); 

25
यह दो का बेहतर जवाब है; डेटाबेस को ऑफ़लाइन लेने से बचा जाता है और स्वीकृत उत्तर हमेशा काम नहीं करता है (कभी-कभी यह सब कुछ वापस नहीं कर सकता है)।
मार्क हेंडरसन

3
एकत्रीकरण के लिए इतना अच्छा जवाब kill बयानों के । मैं प्रत्येक प्रक्रिया को मारने के लिए एक कर्सर का उपयोग करता हूं, जो निश्चित रूप से कुशल नहीं है। इस उत्तर में प्रयुक्त तकनीक शानदार है।
सईद नेमाटी

मैं मार्क से सहमत हूं। यह विधि स्वीकृत उत्तर होनी चाहिए क्योंकि यह डेटाबेस के लिए अधिक सुरुचिपूर्ण और कम प्रभावकारी है।
ऑस्टिन एस।

3
अच्छा एक और जल्दी। केवल समस्या ही सिस्टम स्पिड हो सकती है, इसलिए u WHD = db_id ('My_db') और स्पाइड> 50 जोड़ सकते हैं
सौरभ सिन्हा

1
@FrenkyB आपको स्क्रिप्ट चलाने से पहले डेटाबेस संदर्भ को बदलना होगा। उदाहरण के लिए:USE [Master]
एलेक्सा

133
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


9
ताज्जुब है USE masterकि यह महत्वपूर्ण था। मैं इसे (Duh!) से कनेक्ट करते समय db को छोड़ने का प्रयास कर रहा था। धन्यवाद!
22

9
यदि आप उपयोग करते SET OFFLINEहैं तो आपको db फ़ाइलों को मैन्युअल रूप से हटाना होगा।
मटैलिकांड्र

5
alter database YourDatabaseName set SINGLE_USER with rollback immediateबेहतर नहीं होगा ? यदि आप इसे OFFLINE(@mattalxndr राज्यों के रूप में) सेट करते हैं , तो फ़ाइलें डिस्क पर छोड़ दी जाएंगी, लेकिन SINGLE_USERआपके कनेक्शन के साथ केवल एक ही बची drop database YourDatabaseNameरहेगी , और अभी भी फ़ाइलों को हटा देगी।
कीथ

1
@ स्क्रिप्ट में, आप DB से कनेक्ट नहीं हैं, इसलिए यह "आपका कनेक्शन" नहीं है, लेकिन कुछ अन्य जो बचे रहेंगे। इसके तुरंत बाद set offline, आप set onlineबचे हुए फ़ाइल समस्या से बचने के लिए जारी कर सकते हैं (हाँ, एक दौड़ की स्थिति की संभावना है)।
ivan_pozdeev

2
धन्यवाद! मुझे महसूस नहीं हुआ कि इस डेटाबेस पर पहले निष्पादित SQL प्रबंधन स्टूडियो में sql स्टेटमेंट के साथ कुछ टैब, मेरे db को उपयोग में बताए जाने का कारण बन रहा था। मास्टर का उपयोग करें, और जाओ, सब कुछ काम किया!
पलक

26

आप SSMS को निम्न करके स्क्रिप्ट प्रदान कर सकते हैं:

  1. SSMS में एक डेटाबेस पर राइट-क्लिक करें और हटाएं चुनें
  2. संवाद में, "मौजूदा कनेक्शन बंद करें" के लिए चेकबॉक्स देखें।
  3. संवाद के शीर्ष पर स्क्रिप्ट बटन पर क्लिक करें।

स्क्रिप्ट कुछ इस तरह दिखाई देगी:

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

3
मैं किसी भी उपयोगकर्ता के लिए एकल उपयोगकर्ता मोड में डेटाबसे लेने की सिफारिश नहीं करूंगा क्योंकि इससे आपको कुछ एप्लिकेशन उपयोगकर्ता के लिए वर्तमान कनेक्शन ढीला हो सकता है और उन उपयोगकर्ताओं को खोजने के लिए अनावश्यक रूप से परेशानी हो सकती है और यदि डीबी से कनेक्शन हैं तो कुछ ही समय में यू को एसक्यूएल सर्वर को पुनरारंभ करना होगा। इतनी बार।
सौरभ सिन्हा

7

थोड़ा ज्ञात: GO sql स्टेटमेंट पिछली कमांड को दोहराने के लिए कई बार एक पूर्णांक ले सकता है।

तो फिर आप:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

फिर:

USE [DATABASENAME]
GO 2000

यह 2000 बार यूएसई कमांड को दोहराएगा, अन्य सभी कनेक्शनों पर गतिरोध को बल देगा, और एकल कनेक्शन का स्वामित्व लेगा। (अपनी क्वेरी विंडो को अपनी इच्छानुसार करने के लिए एकमात्र एक्सेस देना।)


2
GO एक TSQL कमांड नहीं है, लेकिन एक विशेष कमांड केवल sqlcmd और osql उपयोगिताओं और SSMS द्वारा मान्यता प्राप्त है।
विगत

4

मेरे अनुभव के लिए, SINGLE_USER का उपयोग करने में सबसे अधिक मदद मिलती है, हालांकि, किसी को सावधान रहना चाहिए: मेरे पास ऐसे अनुभव हैं जिनमें मैं SINGLE_USER कमांड शुरू करता हूं और समय समाप्त हो गया है ... जाहिर है कि एक और 'उपयोगकर्ता' को भूल गया था SINGLE_USER पहुंच, मुझे नहीं। यदि ऐसा होता है, तो आप एक कठिन नौकरी के लिए डेटाबेस तक पहुँच प्राप्त करने की कोशिश कर रहे हैं (मेरे मामले में, यह एसक्यूएल डेटाबेस के साथ सॉफ़्टवेयर के लिए चलने वाली एक विशिष्ट सेवा थी, जो मैंने करने से पहले SINGLE_USER पहुंच प्राप्त कर ली थी)। मुझे लगता है कि सबसे विश्वसनीय तरीका होना चाहिए (इसके लिए व्रत नहीं किया जा सकता है, लेकिन यह वही है जो मैं आने वाले दिनों में परीक्षण करूंगा), वास्तव में है:
- ऐसी सेवाओं को रोकें जो आपकी पहुंच में बाधा उत्पन्न कर सकती हैं (यदि कोई हो)
- सभी कनेक्शन बंद करने के लिए ऊपर 'किल' स्क्रिप्ट का उपयोग करें
- इसके बाद तुरंत डेटाबेस को एक सेट करें
- फिर रिस्टोर करें


यदि SINGLE_USER कमांड आपके बैच के समान है (स्क्रिप्टेड) ​​कमांड को पुनर्स्थापित करें - एक GO स्टेटमेंट द्वारा अलग नहीं किया गया है! - तब कोई अन्य प्रक्रिया मेरे अनुभव में एकल उपयोगकर्ता पहुंच को नहीं पकड़ सकती है। हालाँकि, मुझे आज रात पकड़ा गया क्योंकि सेट-सिंगल-यूज़र की मेरी रात की निर्धारित नौकरी; बहाल; सेट-मल्टी-यूज़र ने उड़ा दी। एक अन्य प्रक्रिया में मेरी bak फ़ाइल (smh) तक अनन्य फ़ाइल पहुंच थी और इसलिए पुनर्स्थापना विफल रही, इसके बाद SET MULTI_USER विफल हो गया ... जिसका अर्थ है कि जब मुझे रात के बीच में खून साफ ​​करने के लिए बुलाया गया, तो किसी और के पास SLELE_USER पहुंच थी और मारना पड़ा।
रोस प्रेसर

3

मैथ्यू की सर्वोच्च कुशल स्क्रिप्ट को dm_exec_session DMV का उपयोग करने के लिए अद्यतन किया गया है, जो कि हटाए गए sysprocesses सिस्टम टेबल की जगह है:

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 लूप का उपयोग करके वैकल्पिक (यदि आप प्रति निष्पादन किसी अन्य संचालन को संसाधित करना चाहते हैं):

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

स्वीकृत उत्तर में यह दोष है कि यह ध्यान में नहीं आता है कि एक डेटाबेस को एक कनेक्शन द्वारा लॉक किया जा सकता है जो एक क्वेरी को निष्पादित कर रहा है जिसमें एक से जुड़े एक डेटाबेस के अलावा अन्य तालिकाओं को शामिल किया गया है।

यह मामला हो सकता है यदि सर्वर आवृत्ति में एक से अधिक डेटाबेस हों और क्वेरी प्रत्यक्ष या अप्रत्यक्ष रूप से (उदाहरण के लिए समानार्थी के माध्यम से) एक से अधिक डेटाबेस आदि में तालिकाओं का उपयोग करें।

इसलिए मुझे लगता है कि कभी-कभी syslockinfo का उपयोग करना बेहतर होता है ताकि मारने के लिए कनेक्शन ढूंढ सकें।

इसलिए मेरा सुझाव एलेक्सक से स्वीकृत उत्तर के नीचे भिन्नता का उपयोग करना होगा:

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

यह! यद्यपि मैं व्यक्तिगत sys.dm_tran_locksरूप syslockinfoसे तालिका का उपयोग करता हूं जैसा कि अप्रचलित है, इसके अलावा, आप केवल मामले में अपने वर्तमान @@ SPID को बाहर करना चाह सकते हैं।
deroby

1

आपको हत्या प्रक्रियाओं के दौरान अपवादों के बारे में सावधान रहना चाहिए। तो आप इस स्क्रिप्ट का उपयोग कर सकते हैं:

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 ने शानदार जवाब लिखा । मैं सिर्फ अपने दो सेंट जोड़ना चाहता हूं। नीचे दिए गए कोड पूरी तरह से @ एलेक्सके के उत्तर पर आधारित है, अंतर यह है कि आप उपयोगकर्ता को निर्दिष्ट कर सकते हैं और अंतिम बैच निष्पादित होने के बाद का समय (ध्यान दें कि कोड मास्टर के बजाय sysinos_exec_session का उपयोग करता है):

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)

इस उदाहरण में केवल उपयोगकर्ता usrDBTest की प्रक्रिया है जिसे अंतिम बैच को 1 घंटे से अधिक पहले निष्पादित किया गया था, उसे मार दिया जाएगा।


1

आप इस तरह से कर्सर का उपयोग कर सकते हैं :

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

मैंने उस बारे में अपने ब्लॉग में यहाँ लिखा है: 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

मैंने नीचे सरल कोड के साथ सफलतापूर्वक परीक्षण किया है

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

2
set SINGLE_USERजब पहले से ही एक सक्रिय कनेक्शन था, तो मेरे पास मुद्दे थे ।
ivan_pozdeev
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.