it-swarm.asia

برنامج نصي لقتل جميع الاتصالات بقاعدة بيانات (أكثر من RESTRICTED_USER ROLLBACK)

لدي قاعدة بيانات تطوير تعيد نشرها بشكل متكرر من مشروع قاعدة بيانات Visual Studio (عبر TFS Auto Build).

أحيانًا عندما أقوم بتشغيل الإصدار الخاص بي ، يحدث هذا الخطأ:

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 يدويًا والبدء في قتل الاتصالات ، لكنني بحاجة إلى طريقة تلقائية للقيام بذلك في الإنشاء التلقائي. (على الرغم من هذه المرة ، فإن الاتصال الخاص بي هو الوحيد الذي أحاول إسقاطه على وحدة قاعدة البيانات.)

هل هناك برنامج نصي يمكنه إسقاط قاعدة البيانات الخاصة بي بغض النظر عمن يتصل؟

185
Vaccano

محدث

ل MS SQL Server 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);

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

المرجع: http://msdn.Microsoft.com/en-us/library/bb522682٪28v=sql.105٪29.aspx

123
Chains

يمكنك الحصول على البرنامج النصي الذي يوفره SSMS عن طريق القيام بما يلي:

  1. انقر بزر الماوس الأيمن فوق قاعدة بيانات في SSMS واختر حذف
  2. في مربع الحوار ، حدد مربع الاختيار "لإغلاق الاتصالات الحالية".
  3. انقر فوق الزر Script أعلى مربع الحوار.

سيبدو النص مثل هذا:

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

غير معروف: يمكن أن تأخذ عبارة GO sql عددًا صحيحًا لعدد مرات تكرار الأمر السابق.

إذا كنت:

ALTER DATABASE [DATABASENAME] SET SINGLE_USER
GO

ثم:

USE [DATABASENAME]
GO 2000

سيؤدي هذا إلى تكرار الأمر USE 2000 مرة ، وفرض حالة توقف تام على كافة الاتصالات الأخرى ، والاستيلاء على اتصال واحد. (منح نافذة الاستعلام الخاصة بك وصولاً منفردًا للقيام بما تريده.)

6
Sodoshi

تم تحديث البرنامج النصي الفعال للغاية الخاص بـ Matthew لاستخدام dm_exec_sessions 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;
3
Chris Bates

حسب تجربتي ، فإن استخدام SINGLE_USER يساعد في معظم الأوقات ، ومع ذلك ، ينبغي للمرء أن يكون حذراً: لقد واجهت مناسبات بين وقت بدء تشغيل أمر SINGLE_USER والوقت الذي انتهى فيه ... يبدو أن "مستخدمًا" آخر قد حصل على وصول SINGLE_USER ، وليس أنا. إذا حدث ذلك ، فأنت في مهمة صعبة تحاول الحصول على حق الوصول إلى قاعدة البيانات مرة أخرى (في حالتي ، كانت خدمة محددة تعمل مع برنامج مزود بقواعد بيانات SQL تم تعليق وصول SINGLE_USER قبله). ما أعتقد أنه ينبغي أن يكون الطريقة الأكثر موثوقية (لا يمكنني أن أؤكد ذلك ، ولكن هذا ما سأختبره في الأيام القادمة) ، هو في الواقع:
- إيقاف الخدمات التي قد تتداخل مع وصولك (إن وجد)
- استخدم البرنامج النصي "قتل" أعلاه لإغلاق جميع الاتصالات
- تعيين قاعدة البيانات إلى single_user مباشرة بعد ذلك
- ثم القيام الاستعادة

3
Sacha

يمكنك استخدام المؤشر مثل هذا:

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

1
Filip Holub

يجب أن تكون حذرا حول الاستثناءات أثناء عمليات القتل. لذلك يمكنك استخدام هذا البرنامج النصي:

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

تحتوي الإجابة المقبولة على عيب أنه لا يأخذ في الاعتبار أنه يمكن تأمين قاعدة بيانات بواسطة اتصال يقوم بتنفيذ استعلام يتضمن جداول في قاعدة بيانات غير تلك المتصلة.

قد يكون هذا هو الحال إذا كان مثيل الخادم يحتوي على أكثر من قاعدة بيانات وكان الاستعلام يستخدم بشكل مباشر أو غير مباشر (على سبيل المثال من خلال المرادفات) الجداول في أكثر من قاعدة بيانات واحدة وما إلى ذلك.

لذلك أجد أنه من الأفضل أحيانًا استخدام syslockinfo للعثور على الاتصالات اللازمة للقتل.

لذلك سيكون اقتراحي هو استخدام الصيغة أدناه للإجابة المقبولة من AlexK:

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 كتب عظيم إجابة . أريد فقط أن أضيف سنتي. يعتمد الرمز أدناه تمامًا على إجابة @ AlexK ، والفرق هو أنه يمكنك تحديد المستخدم ووقت منذ آخر دفعة تم تنفيذها (لاحظ أن الرمز يستخدم sys.dm_exec_sessions بدلاً من 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)

في هذا المثال ، سيتم قتل عملية المستخدم usrDBTest التي تم تنفيذ آخر دفعة لها قبل أكثر من ساعة.

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