Large SQL script calls every 5min that is crashing IIS pool? - c#

Context:
I have a dozen of servers.
Each server have a IIS with a site that executes the following large SQL script every 5 minutes.
On some servers, the pool that hosts the site crash. The pool contains this site only.
I need to recycle the pool after each crash... with my hands currently.
So there is an issue with the site and, I think, with the large SQL script.
The C# code that calls the SQL script:
string root = AppDomain.CurrentDomain.BaseDirectory;
string script = File.ReadAllText(root + #"..\SGBD\select_user_from_all_bases.sql").Replace("$date", dtLastModif);
string connectionString = #"Data Source=(local);Integrated Security=SSPI";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var command = new SqlCommand(script, connection);
var reader = command.ExecuteReader();
var users = new List<UserModel>();
while (reader.Read())
{
users.Add(new UserModel()
{
dbName = String.Format("{0}", reader[0]),
idExternal = int.Parse(String.Format("{0}", reader[1])),
firstname = String.Format("{0}", reader[2]),
lastname = String.Format("{0}", reader[3]),
login = String.Format("{0}", reader[4]),
password = String.Format("{0}", reader[5]),
dtContractStart = reader[6] != DBNull.Value ? (DateTime?)reader[6] : null,
dtContractEnd = reader[7] != DBNull.Value ? (DateTime?)reader[7] : null,
emailPro = String.Format("{0}", reader[8]),
emailPerso = String.Format("{0}", reader[9])
});
}
return users;
}
And the SQL script:
USE master
DECLARE db_names CURSOR FOR
SELECT name FROM sysdatabases WHERE [name] LIKE 'FOO_%' AND [name] NOT LIKE 'FOO_TRAINING_%'
DECLARE #db_name NVARCHAR(100)
DECLARE #query NVARCHAR(MAX)
DECLARE #queryFinal NVARCHAR(MAX)
SET #query = ''
OPEN db_names
FETCH NEXT FROM db_names INTO #db_name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = #query + 'SELECT ''' + #db_name + ''', id_salarie, nom, prenom, login COLLATE SQL_Latin1_General_CP1_CI_AS, password COLLATE SQL_Latin1_General_CP1_CI_AS, date_arrivee, date_depart, email COLLATE SQL_Latin1_General_CP1_CI_AS, persoMail COLLATE SQL_Latin1_General_CP1_CI_AS FROM [' + #db_name + '].dbo.utilisateurs WHERE dt_last_modif >= ''$date'' UNION '
FETCH NEXT FROM db_names INTO #db_name
END
DEALLOCATE db_names
SET #queryFinal = left(#query, len(#query)-6)
EXEC sp_executesql #queryFinal
More information about servers:
Server0 : 8 databases, 1050 users, no crash
Server1 : 88 databases, 18954 users, crash often
Server2 : 109 databases, 21897 users, crash often
Server3 : 26 databases, 1612 users, no crash
etc
Questions :
What is the issue with the script ? Any idea how I can stop crashs ?
And if no solution, how can I automatically recycle the pool?

Have you tried to make shure that the reader is cloesd after usage, too?
using(var reader = command.ExecuteReader()) { ...
I am not shure if the closed connection
using (var connection = new SqlConnection(connectionString))
takes care of the command and the reader resources.

I would do a few things here... if your problem is that persistent. First, I WOULD NOT generate one complete sql query trying to get data from all those tables all at once. Next, the queries are querying, and implied might be trying to LOCK the records associated with the query for POSSIBLE update... even though you are not probably going to be doing that.
I would add a WITH (NOLOCK) on the from tables.
select columns from yourTable WITH(NOLOCK) where...
This prevents any overhead with locking all the pages associated with the query.
Now, how to better handle your loop. Immediately BEFORE your fetch loop, I would create a temp table of the expected output results... something like
(unsure of column name lenghts for your structures...
create #C_TempResults
( fromDBName char(20),
id_salarie int,
nom char(10),
prenom char(10),
login char(10),
password char(10),
date_arivee datetime,
date_depart datetime,
email char(60),
persoMail char(60) );
then, in your loop where you are already cycling through all the tables you are querying, instead of building a concatenated SQL statement to execute at the end, just run ONE AT A TIME, and insert into the temp table like...
(same beginning to prepare your fetch cursor...)
BEGIN
SET #query = 'INSERT INTO #C_TempResults '
+ ' SELECT ''' + #db_name + ''' as fromDBName, id_salarie, nom, prenom, '
+ 'login COLLATE SQL_Latin1_General_CP1_CI_AS, '
+ 'password COLLATE SQL_Latin1_General_CP1_CI_AS, '
+ 'date_arrivee, date_depart, '
+ 'email COLLATE SQL_Latin1_General_CP1_CI_AS, '
+ 'persoMail COLLATE SQL_Latin1_General_CP1_CI_AS '
+ 'FROM [' + #db_name + '].dbo.utilisateurs WITH (NOLOCK) '
+ 'WHERE dt_last_modif >= ''$date'' ';
-- Run this single query now, get the data and release any "lock" resources
EXEC sp_executesql #queryFinal
-- now, get the next database to query from and continue
FETCH NEXT FROM db_names INTO #db_name
END
DEALLOCATE db_names
-- FINALLY, just run your select from the temp table that has everything all together...
select * from #C_TempResults;
-- and get rid of your "temp" table
drop table #C_TempResults;

Related

Different behaviors from SQL Server than from Visual Studio

I am currently developing a C# web application that executes a SQL Server stored procedure. The stored procedure is an OpenRowSet query, that pulls data from an excel file and then inputs the data into a SQL Server table.
If I execute the stored procedure in an SSMS query window everything works fine:
(160 rows affected) Completion time: 2021-09-07T15:54:14.1558312-04:00
If I execute the stored procedure from Visual Studio, I get the following message:
OLE DB provider "Microsoft.ACE.OLEDB.12.0" for linked server "(null)" returned message "The Microsoft Access database engine could not find the object 'Table$'. Make sure the object exists and that you spell its name and the path name correctly. If 'Table$' is not a local object, check your network connection or contact the server administrator.
I had assumed this was a permissions issue, but after running a profile on the call, Both the SSMS execution and the VS call are executed by the same user.
Here is the C# code:
public class DataPut
{
public static string _connection = ConfigurationManager.ConnectionStrings["_connection"].ConnectionString;
SqlConnection conn = new SqlConnection(_connection);
public void putReports(string sheetname, string filename, string tablename)
{
string query = "sp_DataImport";
SqlCommand cmd = new SqlCommand(query, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#tablename", tablename);
cmd.Parameters.AddWithValue("#SheetName", sheetname);
cmd.Parameters.AddWithValue("#FilePath", filename);
conn.Open();
//cmd.CommandTimeout = 120;
cmd.ExecuteNonQuery();
conn.Close();
}
}
Here is the stored procedure:
CREATE PROCEDURE [dbo].[sp_DataImport]
#SheetName varchar(20),
#FilePath varchar(200),
#TableName varchar(50)
AS
BEGIN
DECLARE #SQL nvarchar(1000)
IF OBJECT_ID (#TableName,'U') IS NOT NULL
SET #SQL = 'insert INTO [' + #TableName +'] SELECT * FROM OPENROWSET(''Microsoft.ACE.OLEDB.12.0'',
''Excel 12.0;Database=' + #FilePath + ''',
''SELECT * FROM [' + #SheetName + '] Where [Column] = ''''Value'''' AND [Column] =''''Value'''' AND [Column] = ''''Value'''''')'
ELSE
SET #SQL = 'SELECT * INTO [' + #TableName +'] FROM OPENROWSET(''Microsoft.ACE.OLEDB.12.0'',
''Excel 12.0;Database=' + #FilePath + ''',
''SELECT * FROM [' + #SheetName + '] Where [Column] = ''''Value'''' AND [Column] =''''Value'''' AND [Column] = ''''Value'''''')'
PRINT #SQL
EXEC sp_executesql #SQL
END
Any idea why these would have different behaviors?

How to change and Work on a selected year database and create new database on every year

I am working on an Accounting project using C#/.NET,Libraries,Reportings,etc
and MS-SQL Server. so when a running financial period is closed on every year, example
(30july2017---30july2018),
I want to transfer all the data, all the transactions records of that year in a new database(you can say a new copy
of that database), and save all that record in a drive for the previous long
term evidence purposes. and create New Database for new financial year period and process new transactions etc.
my basic Login UI consist of few things,
username (text)
pasword(text)
financial year Dropdown("2016-2017","2017-2018").
when I login Using 2017-2018, Database is selected to the newly created and
perform transactions into this. But if I go Back Period (2016-2017) then It
should not allow any EDIT in that Database.
the lookup of my description and the desire answer is based on this.
not a UI Code is enough but SQL creating Database using this UI will be the answer for which I am finding here.
any one who can help me on this issue,
from the Question, I can tell that you are handling accounts kind of data database like on closing one database some amount will be added to opening is next year and so forth that can be handled on application-level but as for creating the database, you can just simply call a stored procedure. here is a Procedure that might help.
create PROCEDURE [dbo].[CreateNewCompany]
#CompName as varchar(max) ,-- for create New Company
#BackupFilePath as varchar(max) -- Backup file path for Which Company DB is a copy for a new company
As
BEGIN
DECLARE #COL1 VARCHAR(256),#COL2 VARCHAR(256) -- Filename for backup
DECLARE #Lgfilenm1 VARCHAR(256) -- LogicalFilename for backup
DECLARE #Lgfilenm2 VARCHAR(256) -- LogicalFilename for backup
DECLARE #RestorePath VARCHAR(256) -- PhysicalFilename for backup
DECLARE #i int
set #i = 0
--- Make Temp Table
create table TmtNewDB ( LogicalName nvarchar(128),PhysicalName nvarchar(260),Type char(1),FileGroupName nvarchar(128),Size numeric(20,0)
,MaxSize numeric(20,0),Fileid tinyint,CreateLSN numeric(25,0),DropLSN numeric(25, 0),UniqueID uniqueidentifier,ReadOnlyLSN numeric(25,0),
ReadWriteLSN numeric(25,0),BackupSizeInBytes bigint,SourceBlocSize int,FileGroupId int,LogGroupGUID uniqueidentifier,
DifferentialBaseLSN numeric(25,0),DifferentialBaseGUID uniqueidentifier,IsReadOnly bit,IsPresent bit)
insert TmtNewDB
EXEC ('restore filelistonly from disk = ''' + #BackupFilePath + '''')
declare #a varchar(max),#posi int,#le int,#file varchar(max)
DECLARE db_newdbb CURSOR FOR
select LogicalName,PhysicalName from TmtNewDB
OPEN db_newdbb
FETCH NEXT FROM db_newdbb INTO #COL1,#COL2
WHILE ##FETCH_STATUS = 0
BEGIN
if #i=0
begin
set #Lgfilenm1=#COL1
set #posi = CHARINDEX('\',REVERSE(#COL2))
set #RestorePath=left(#COL2,len(#COL2)- #posi +1 )
end
else if #i=1
begin
set #Lgfilenm2 = #COL1
end
set #i = #i + 1
FETCH NEXT FROM db_newdbb INTO #COL1,#COL2
END
CLOSE db_newdbb
DEALLOCATE db_newdbb
declare #sql varchar(max)
set #sql= 'RESTORE DATABASE ' + #CompName + ' FROM DISK = ''' + #BackupFilePath + ''' WITH FILE = 1,
MOVE N''' + #Lgfilenm1 + ''' TO N''' + #RestorePath+ #CompName + '.mdf' + ''',
MOVE N''' + #Lgfilenm2 + ''' TO N''' + #RestorePath+ #CompName + '_Log.ldf' + ''''
-- print #sql
-- #CompName check Company file already is created or not
IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = #CompName OR name = #CompName)))
begin
print 'New Company Already Created------->newdbb'
end
else
begin
exec (#sql)
print 'New Company Created Successfully------->newdbb'
end
END

Encrypt the stored procedure created by SqlDependency

I created a SqlDependency so that an event would fire when the results of a particular query change.
// Create a command
SqlConnection conn = new SqlConnection(connectionString);
string query = "SELECT MyColumn FROM MyTable;";
SqlCommand cmd = new SqlCommand(query, conn)
cmd.CommandType = CommandType.Text;
// Register a dependency
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange += DependencyOnChange;
When this code executes, a stored procedure is automatically created with a name like
SqlQueryNotificationStoredProcedure-82ae1b92-21c5-46ae-a2a1-511c4f849f76
This procedure is unencrypted, which violates requirements I have been given. I have two options:
Convince the customer that it doesn't matter that the auto generated procedure is unencrypted because it only does cleanup work and contains no real information (thanks to ScottChamberlain for pointing this out).
Find a way to encrypt the stored procedure generated by SqlDependency.
How can I accomplish option 2?
Contents of the stored procedure in question:
CREATE PROCEDURE [dbo].[SqlQueryNotificationStoredProcedure-b124707b-23fc-4002-aac3-4d52a71c5d6b]
AS
BEGIN
BEGIN TRANSACTION;
RECEIVE TOP (0) conversation_handle
FROM [SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b];
IF (
SELECT COUNT(*)
FROM [SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b]
WHERE message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer'
) > 0
BEGIN
IF (
(
SELECT COUNT(*)
FROM sys.services
WHERE NAME = 'SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b'
) > 0
)
DROP SERVICE [SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b];
IF (OBJECT_ID('SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b', 'SQ') IS NOT NULL)
DROP QUEUE [SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b];
DROP PROCEDURE [SqlQueryNotificationStoredProcedure-b124707b-23fc-4002-aac3-4d52a71c5d6b];
END
COMMIT TRANSACTION;
END
GO
Create a DDL trigger that checks if a procedure with a name like "SqlQueryNotificationStoredProcedure-" is being created, and if so, immediately alter it WITH ENCRYPTION instead:
CREATE TRIGGER [TR_EncryptQueryNotificationProcedures]
ON DATABASE
AFTER CREATE_PROCEDURE, ALTER_PROCEDURE
AS
BEGIN
SET ARITHABORT ON;
SET NOCOUNT ON;
IF TRIGGER_NESTLEVEL() > 1 RETURN;
-- For debugging purposes only
PRINT CONVERT(NVARCHAR(MAX), EVENTDATA());
DECLARE #DatabaseName NVARCHAR(128);
SET #DatabaseName = EVENTDATA().value(
'(/EVENT_INSTANCE/DatabaseName)[1]', 'NVARCHAR(128)'
);
DECLARE #Schema NVARCHAR(128);
SET #Schema = EVENTDATA().value(
'(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(128)'
);
DECLARE #Name NVARCHAR(128);
SET #Name = EVENTDATA().value(
'(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(128)'
);
DECLARE #Definition NVARCHAR(MAX);
SELECT #Definition =
OBJECT_DEFINITION(
OBJECT_ID(
QUOTENAME(#DatabaseName) + '.' +
QUOTENAME(#Schema) + '.' +
QUOTENAME(#Name),
'P'
)
)
;
-- If the sproc is already encrypted, we can't do anything with it
IF #Definition IS NULL RETURN;
SELECT #Definition = STUFF(
#Definition,
CHARINDEX('CREATE', #Definition),
LEN('CREATE'),
'ALTER'
);
IF
#Name LIKE 'SqlQueryNotificationStoredProcedure-%' AND
-- this should always be false since we can't read encrypted definitions,
-- but just to make sure
#Definition NOT LIKE '%WITH ENCRYPTION AS BEGIN%'
BEGIN;
SET #Definition = REPLACE(
#Definition, 'AS' + CHAR(13) + CHAR(10) + 'BEGIN',
'WITH ENCRYPTION AS BEGIN'
);
EXEC (#Definition);
END;
END;
GO
ENABLE TRIGGER [TR_EncryptQueryNotificationProcedures] ON DATABASE;
Disclaimer: not tested against an actual dependency notification, but the basic idea is sound. It's quite brittle because it depends on the exact form of the procedure, of course -- making it more robust is possible, but tedious.

How to change a sql login password with variables

I am trying to update the password for an existing SQL login using Alter LOGIN
I know the following works
ALTER LOGIN [username1] WITH PASSWORD = 'somenewpassword123';
However when I try to use a local variable
DECLARE #newpass nvarchar(max);
SET #newpass = 'P#ssw0rd12345';
ALTER LOGIN [username1] WITH PASSWORD = #newpass;
This fails. Adding [] braces to the variable seems to resolve this within the SSMS query editor however using this programmaticlly by writing out the query in C# it fails as the above statement with the same error ( syntax error at PASSWORD)
Code within c# app
public static int UpdateSqlLoginPassword(DbContext context, string loginName, string password)
{
try
{
string updatePassword =
#" SET NOCOUNT ON
DECLARE #loginName AS nvarchar(max) = {0}
DECLARE #password AS nvarchar(max) = {1}
EXEC('
USE master
ALTER LOGIN ['+ #loginName + '] WITH PASSWORD = ['+ #password + ']
')";
return context.Database.ExecuteSqlCommand(updatePassword, loginName, password);
}
catch (Exception)
{
return -2;
}
}
I have also tried to hash the password (thinking that was the issue with the variable) but the syntax here is not being accepted
DECLARE #newpass nvarchar(max);
SET #newpass = 'P#ssw0rd12345';
DECLARE #hashedpass varbinary(max);
SET #hashedpass = HASHBYTES('SHA1', CONVERT(nvarchar(max),#newpass));
ALTER LOGIN [newuser10] WITH PASSWORD = #hashedpass HASHED;
SELECT #hashedpass;
Can anyone help me understand how to update a login's password in sql using a variable instead of a fixed value?
thanks in advance
Update
Based upon a suggestion from Charlie I also tried the following
public static int UpdateSqlLoginPassword(DbContext context, string loginName, string password)
{
try
{
string updatePassword =
#"ALTER LOGIN [' + #loginName +'] WITH PASSWORD = #password ";
return context.Database.ExecuteSqlCommand(updatePassword, new SqlParameter("loginName", loginName), new SqlParameter("password", password));
}
catch (Exception)
{
return -2;
}
}
This still generates a sqlException Incorrect Syntax new '#password'.
If I brace the parameter
public static int UpdateSqlLoginPassword(DbContext context, string loginName, string password)
{
try
{
string updatePassword =
#"ALTER LOGIN [' + #loginName +'] WITH PASSWORD = [' + #password +']";
return context.Database.ExecuteSqlCommand(updatePassword, new SqlParameter("loginName", loginName), new SqlParameter("password", password));
}
catch (Exception)
{
return -2;
}
}
I then generate a sqlException Incorrect syntax near PASSWORD.
Update2
Using the updated suggestions from Charlie I attempted to use the QuoteName function
string sql = #"DECLARE #sql NVARCHAR(500)
SET #sql = 'ALTER LOGIN ' + QuoteName(#loginName) +
' WITH PASSWORD = ' + QuoteName(#password, '''')
EXEC #sql";
return context.Database.ExecuteSqlCommand(sql, new SqlParameter("loginName", loginName), new SqlParameter("password", password));
While it appears that the query string is properly formed the following SQLException is thrown
*The name 'ALTER LOGIN [newuser10] WITH PASSWORD = 't#P#ssw0rd'' is not a valid identifier.
EDIT
After some more reading the error was generated by a syntax error wrapping the #sql allows the query to execute with no errors
string sql = #"DECLARE #sql NVARCHAR(500)
SET #sql = 'ALTER LOGIN ' + QuoteName(#loginName) +
' WITH PASSWORD = ' + QuoteName(#password, '''')
EXEC(#sql)";
On a side note: by simply building the string and running it as
string updatePassword = "USE MASTER ALTER LOGIN [" + loginName + "] WITH PASSWORD = '" + password + "'";
return context.Database.ExecuteSqlCommand(updatePassword);
the above is also a workaround and updates the sql login. While the implementation of this code minimizes the potential for sql injections this is not the most desirable approach.
-Thanks
You need to use parameters at the DbContext level. See this answer for more details, but, here's a code example (adapted from that same page):
string sql = "ALTER LOGIN #loginName WITH PASSWORD = #password";
ctx.Database.ExecuteSqlCommand(
sql,
new SqlParameter("loginName", loginName),
new SqlParameter("password", password));
The purpose of using the parameters here (and everywhere) is to prevent a SQL injection attack. This is especially important given that you are writing code that changes a password.
UPDATE
The ALTER LOGIN statement won't work with variables; it must be done through dynamic SQL. Here's an example of the updated code:
string sql = #"DECLARE #sql NVARCHAR(500)
SET #sql = 'ALTER LOGIN ' + QuoteName(#loginName) +
' WITH PASSWORD= ' + QuoteName(#password, '''')
EXEC #sql ";
ctx.Database.ExecuteSqlCommand(
sql,
new SqlParameter("loginName", loginName),
new SqlParameter("password", password));
Note we're still using the SqlParameters to prevent SQL injection attacks. We are also using the T-SQL method QuoteName to do proper quoting in the SQL we are generating; but this method simply doubles any [ characters (in the first call) or ' characters (in the second). There are many other vectors for a SQL injection attack, so merely relying on QuoteName wouldn't be enough.
I'm using the above answer with Azure SQL and I was getting the "not a valid identifier" error until I surrounded replaced "EXEC #sql" with "EXEC (#sql)". See Msg 203, Level 16, State 2, is not a valid identifier
Additionally, I had to use "ALTER USER" instead of "ALTER LOGIN"
After Preparing SQL query string and executing using c# SQL Command, I was always getting Invalid Identifier error.
It was because QuoteName should get executed before executing change password sql statements.
So I created stored procedure using above solutions then called procedure from c#, it worked for me.
Create procedure usp_updateSqlUsers(#loginName nVarchar(100), #pwd nvarchar(100))
as
begin
DECLARE #sql NVARCHAR(500)
set #sql='Alter LOGIN '+QUOTENAME(#loginName)+' WITH
password=N'+ QUOTENAME(#pwd,'''')
exec sp_sqlexec #sql
end
Then execute from C#
SqlCommand cmd = new SqlCommand("usp_updateSqlUsers", con) {CommandType =
CommandType.StoredProcedure};
var passwordParam = new SqlParameter("#pwd", password);
var sqlLoginParameter = new SqlParameter("#loginName", "SqlLoginName");
cmd.Parameters.Add(passwordParam);
cmd.Parameters.Add(sqlLoginParameter);
cmd.ExecuteNonQuery();

Cannot drop database because it is currently in use

I want to drop a database. I have used the following code, but to no avail.
public void DropDataBase(string DBName,SqlConnection scon)
{
try
{
SqlConnection.ClearAllPools();
SqlCommand cmd = new SqlCommand("ALTER DATABASE " + DBName + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE", scon);
cmd.CommandType = CommandType.Text;
scon.Open();
cmd.ExecuteNonQuery();
scon.Close();
SqlCommand cmddrpdb = new SqlCommand("drop database " + DBName + "", scon);
cmddrpdb.CommandType = CommandType.Text;
scon.Open();
cmddrpdb.ExecuteNonQuery();
scon.Close();
}
catch (Exception ex)
{
MessageBox.Show("DropDataBase : " +ex.Message);
}
}
I am getting Error as cannot drop database because it is currently in use.
Please help me out in the above mentioned issue.
Before dropping a database, you will need to drop all the connections to the target database first.
I have found a solution at http://www.kodyaz.com/articles/kill-all-processes-of-a-database.aspx
DECLARE #DatabaseName nvarchar(50)
SET #DatabaseName = N'YOUR_DABASE_NAME'
DECLARE #SQL varchar(max)
SELECT #SQL = COALESCE(#SQL,'') + 'Kill ' + Convert(varchar, SPId) + ';'
FROM MASTER..SysProcesses
WHERE DBId = DB_ID(#DatabaseName) AND SPId <> ##SPId
--SELECT #SQL
EXEC(#SQL)
It's too late, but it may be useful for future users.
You can use the below query before dropping the database query:
use master go
alter database [MyDatbase] set single_user with rollback immediate
drop database [MyDatabase]
It will work. You can also refer to
How do I specify "close existing connections" in sql script
I hope it will help you :)
Someone connected to the database. Try to switch to another database and then, to drop it:
Try
SP_WHO to see who connected
and KILL if needed
For SQL server mgmt. studio:
Right click database: Properties -> Options -> Restrict Access : Set to "Single User" and perform the drop afterwards
In SQL Server Management Studio 2016, perform the following:
Right click on database
Click delete
Check close existing connections
Perform delete operation
select * from sys.sysprocesses where dbid = DB_ID('Test')
(Replace 'Test' with the name of the database you are trying to drop)
This will tell you which processes are using it.
If you still want to force drop then, the ultimate approach is:
USE master;
GO
ALTER DATABASE Test
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
GO
DROP DATABASE Test;
Hope this helps !
First make your data base offline after that detach it e.g.
Use Master
GO
ALTER DATABASE dbname SET OFFLINE
GO
EXEC sp_detach_db 'dbname', 'true'
If your dropping the database in SQL Management Studio and you get the message, don't forget that you use Master as selected database otherwise your query is also an connection to the database.
USE Master;
GO
DROP DATABASE AdventureWorks;
GO
First check the connected databases
SP_WHO
Second Disconnect your database
DECLARE #DatabaseName nvarchar(50)
SET #DatabaseName = N'your_database_name'
DECLARE #SQL varchar(max)
SELECT #SQL = COALESCE(#SQL,'') + 'Kill ' + Convert(varchar, SPId) + ';'
FROM MASTER..SysProcesses
WHERE DBId = DB_ID(#DatabaseName) AND SPId <> ##SPId
--SELECT #SQL
EXEC(#SQL)
FINALLY DROP IT
drop database your_database
A brute force workaround could be:
Stop the SQL Server Service.
Delete the corresponding .mdf and .ldf files.
Start the SQL Server Service.
Connect with SSMS and delete the database.
I wanted to call out that I used a script that is derived from two of the answers below.
Props to #Hitesh Mistry and #unruledboy
DECLARE #DatabaseName nvarchar(50)
SET #DatabaseName = N'[[[DatabaseName]]]'
DECLARE #SQL varchar(max)
SELECT #SQL = COALESCE(#SQL,'') + 'Kill ' + Convert(varchar, SPId) + ';'
FROM MASTER..SysProcesses
WHERE DBId = DB_ID(#DatabaseName) AND SPId <> ##SPId
EXEC(#SQL)
alter database [[[DatabaseName]]] set single_user with rollback immediate
DROP DATABASE [[[DatabaseName]]]
Using MS SQL Server 2008, in DELETE dialog with Close connection options, this is the generated script, I guess it is the best:
EXEC msdb.dbo.sp_delete_database_backuphistory #database_name = N'YOUR_DATABASE_NAME'
GO
USE [master]
GO
ALTER DATABASE [YOUR_DATABASE_NAME] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
USE [master]
GO
/****** Object: Database [YOUR_DATABASE_NAME] Script Date: 01/08/2014 21:36:29 ******/
DROP DATABASE [YOUR_DATABASE_NAME]
GO
Just wanted to give a vb.net (as with c language if want to convert..) I was having similar problem for uninstal of one of my programs, dropping the DB was bit tricky, yes could get users to go into server drop it using Express, but thats not clean, after few looks around got a perfect little bit of code together...
Sub DropMyDatabase()
Dim Your_DB_To_Drop_Name As String = "YourDB"
Dim Your_Connection_String_Here As String = "SERVER=MyServer;Integrated Security=True"
Dim Conn As SqlConnection = New SqlConnection(Your_Connection_String_Here)
Dim AlterStr As String = "ALTER DATABASE " & Your_DB_To_Drop_Name & " SET OFFLINE WITH ROLLBACK IMMEDIATE"
Dim AlterCmd = New SqlCommand(AlterStr, Conn)
Dim DropStr As String = "DROP DATABASE " & Your_DB_To_Drop_Name
Dim DropCmd = New SqlCommand(DropStr, Conn)
Try
Conn.Open()
AlterCmd.ExecuteNonQuery()
DropCmd.ExecuteNonQuery()
Conn.Close()
Catch ex As Exception
If (Conn.State = ConnectionState.Open) Then
Conn.Close()
End If
MsgBox("Failed... Sorry!" & vbCrLf & vbCrLf & ex.Message)
End Try
End Sub
Hope this helps anyone looking
xChickenx
UPDATE
Using this converter here is the C# version :
public void DropMyDatabase()
{
var Your_DB_To_Drop_Name = "YourDB";
var Your_Connection_String_Here = "SERVER=MyServer;Integrated Security=True";
var Conn = new SqlConnection(Your_Connection_String_Here);
var AlterStr = "ALTER DATABASE " + Your_DB_To_Drop_Name + " SET OFFLINE WITH ROLLBACK IMMEDIATE";
var AlterCmd = new SqlCommand(AlterStr, Conn);
var DropStr = "DROP DATABASE " + Your_DB_To_Drop_Name;
var DropCmd = new SqlCommand(DropStr, Conn);
try
{
Conn.Open();
AlterCmd.ExecuteNonQuery();
DropCmd.ExecuteNonQuery();
Conn.Close();
}
catch(Exception ex)
{
if((Conn.State == ConnectionState.Open))
{
Conn.Close();
}
Trace.WriteLine("Failed... Sorry!" + Environment.NewLine + ex.Message);
}
}
To delete a database even if it's running, you can use this batch file
#echo off
set /p dbName= "Enter your database name to drop: "
echo Setting to single-user mode
sqlcmd -Q "ALTER DATABASE [%dbName%] SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
echo Dropping...
sqlcmd -Q "drop database %dbName%"
echo Completed.
pause
You cannot drop a database currently being used however you can use sp_detach_db stored procedure if you want to remove a database from the server without deleting the database files.
just renaming the DB (to be delete) did the trick for me. it got off the hold of whatever process was accessing the database, and so I was able to drop the database.
Go to available databases section and select master. Then Try DROP DATABASE the_DB_name.
Use this:
/* Delete Database Backup and Restore History from MSDB System Database */
EXEC msdb.dbo.sp_delete_database_backuphistory #database_name = N'[dba]'
GO
/* Query to Get Exclusive Access of SQL Server Database before Dropping the Database */
USE [master]
GO
ALTER DATABASE [dba]
SET SINGLE_USER
WITH
ROLLBACK IMMEDIATE
GO
/* Query to Drop Database in SQL Server */
DROP DATABASE [dba]
GO

Categories