After I read Greg Low’s post and my initial try, I kept on thinking that I need to come up with a solution that in theory is robust. The only way I can think of is to use a CLR stored procedure which will do the following three steps:
1. alter database <blah> set offline with rollback immediate;
2. find all the files that compose the <blah> database, using
select physical_name from master.sys.master_files where database_id = db_id(‘blah’)
3. delete each file found in step 2.
So here is C# code to create the CLR stored procedure:
1: using System;
2: using System.Collections;
3: using System.Data;
4: using System.Data.SqlClient;
5: using System.Data.SqlTypes;
6: using Microsoft.SqlServer.Server;
7: using System.IO;
8: 9: public partial class StoredProcedures
10: { 11: [Microsoft.SqlServer.Server.SqlProcedure]12: public static int usp_DropDB (SqlString db_name)
13: { 14: 15: try
16: {17: using (SqlConnection conn = new SqlConnection("context connection=true"))
18: {19: string sql = "select cnt=count(*) from master.sys.databases where name=@dbname";
20: SqlCommand scmd = new SqlCommand();
21: SqlParameter parmDBName = new SqlParameter("@dbname", SqlDbType.VarChar, 128);
22: parmDBName.Value = db_name.Value; 23: scmd.Parameters.Add(parmDBName); 24: scmd.CommandText = sql; 25: scmd.Connection = conn; 26: conn.Open(); 27: Int32 i = (Int32)scmd.ExecuteScalar(); 28: conn.Close(); 29: scmd.Dispose(); 30: 31: if (i == 1)
32: { 33: 34: sql = "if exists (select * from sys.databases where name=@dbname)";
35: sql = sql + "\r\n alter database [" + db_name.Value + "] set offline with rollback immediate;";
36: 37: SqlCommand cmd = new SqlCommand();
38: SqlParameter pDBName = new SqlParameter("@dbname", SqlDbType.VarChar, 128);
39: pDBName.Value = db_name.Value; 40: 41: 42: cmd.Parameters.Add(pDBName); 43: 44: cmd.CommandText = sql; 45: cmd.Connection = conn; 46: 47: conn.Open(); 48: cmd.ExecuteNonQuery();49: cmd.CommandText = "select physical_name from master.sys.master_files where database_id=db_id(@dbname)";
50: SqlDataReader rd = cmd.ExecuteReader();51: ArrayList alFilePath = new ArrayList();
52: while (rd.Read())
53: {54: alFilePath.Add((string)rd.GetString(0));
55: } 56: conn.Close(); 57: 58: cmd.CommandText = "drop database [" + db_name.Value + "];";
59: 60: conn.Open(); 61: cmd.ExecuteNonQuery(); 62: conn.Close(); 63: 64: for (i = 0; i <= alFilePath.Count - 1; i++)
65: {66: File.Delete((string)alFilePath);
67: } 68: }69: else
70: {71: return 1; // no db found
72: } 73: }74: return 0; // succeed
75: }76: catch
77: {78: return (-1); // -1 meaning error occurred
79: } 80: } 81: }
Using Visual Studio to compile this to DLL file, let’s assume the file is called/located at c:\temp\CLR_SP.dll, and then using the following sql statement to create the CLR procedure:
1: use master
2: create assembly clr_dropDB from 'C:\temp\CLR_SP.dll'
3: with permission_set = External_ACCESS;
4: go
5: 6: create proc dbo.usp_DropDB @db_name nvarchar(128)
7: as external name clr_dropDB.StoredProcedures.usp_DropDB;
8: go
9: 10: -- example
11: use master;
12: declare @id int;
13: exec dbo.usp_dropDB @db_name=N'testdb';
14: print @id;
My dev/test environment is Visual Studio 2012 Professional and SQL Server 2012 Developer Edition.