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
Related
I have a C# application which creates a database, tables using T-SQL code executed by SqlCommand class.
Some scripts which are executed by SqlCommand:
exec sp_configure 'show advanced options', 1;
RECONFIGURE;
exec sp_configure 'clr enabled', 1;
RECONFIGURE;
use FooDatabase;
if exists (select * from sys.objects where name = 'CreateLineString')
drop aggregate dbo.CreateLineString;
if exists (select * from sys.objects where name = 'GeographyUnion')
drop aggregate dbo.GeographyUnion;
if exists (select * from sys.objects where name = 'ConvertToPolygon')
drop function dbo.ConvertToPolygon;
if exists (select * from sys.assemblies where name = 'osm2mssqlSqlExtension')
drop assembly osm2mssqlSqlExtension;
create assembly osm2mssqlSqlExtension FROM 0x4D5A900 /* some numbers more here ...*/
300000 WITH PERMISSION_SET = UNSAFE;
GO
create aggregate dbo.CreateLineString(#lat float,#lon float,#sort int) returns geography
external name osm2mssqlSqlExtension.[OsmImporter.DbExtensions.LineStringBuilder];
GO
create aggregate dbo.GeographyUnion(#geo geography) returns geography
external name osm2mssqlSqlExtension.[OsmImporter.DbExtensions.GeographyUnion];
GO
create function dbo.ConvertToPolygon(#geo geography) returns geography
as external name [osm2mssqlSqlExtension].[OsmImporter.DbExtensions.Functions].ConvertToPolygon;
GO
C# code to execute the above sql code:
protected void ExecuteSqlCmd(string sqlCommand)
{
var sqlCommands = sqlCommand.Split(
new[]
{
"GO"
}, StringSplitOptions.RemoveEmptyEntries);
var connString = Connection.ToString();
using (var con = new SqlConnection() { ConnectionString = Connection.ToString() })
{
foreach (var sql in sqlCommands)
{
con.Open();
using (var cmd = new SqlCommand() { Connection = con })
{
cmd.CommandTimeout = int.MaxValue;
cmd.CommandText = sql;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw;
}
}
con.Close();
}
}
}
When I use the following connection string:
"Data Source=SQL100;Initial Catalog=;Integrated Security=True"
Then I see the following error:
The database owner SID recorded in the master database differs from
the database owner SID recorded in database 'FooDatabase'. You should
correct this situation by resetting the owner of database
'FooDatabase' using the ALTER AUTHORIZATION statement. Configuration
option 'show advanced options' changed from 1 to 1. Run the
RECONFIGURE statement to install. Configuration option 'clr enabled'
changed from 1 to 1. Run the RECONFIGURE statement to install. Changed
database context to 'FooDatabase'.
If I use the following connection string, there is no error:
"Data Source=SQL100;Initial Catalog=;User ID=foouser;Password=foopassword
What am I doing wrong? How is it possible to solve this problem? Any help would be greatly appreciated!
UPDATE:
I've tried to use the accepted answer from this question, however I see the following error:
The proposed new database owner is already a user or aliased in the
database. The database owner SID recorded in the master database
differs from the database owner SID recorded in database
'FooDatabase'. You should correct this situation by resetting the
owner of database 'FooDatabase' using the ALTER AUTHORIZATION
statement. Configuration option 'show advanced options' changed from 1
to 1. Run the RECONFIGURE statement to install. Configuration option
'clr enabled' changed from 1 to 1. Run the RECONFIGURE statement to
install. Changed database context to 'FooDatabase'.
This problem can arise when a database restored from a backup and the SID of the database owner does not match the owners SID listed in the master database.Try This One
DECLARE #Cmd VARCHAR(MAX) = 'ALTER AUTHORIZATION ON DATABASE::[<<DatabaseName>>] TO
[<<LoginName>>]'
SELECT #Cmd = REPLACE(REPLACE(#Cmd
, '<<DatabaseName>>', SD.Name)
, '<<LoginName>>', SL.Name)
FROM master..sysdatabases SD
JOIN master..syslogins SL ON SD.SID = SL.SID
WHERE SD.Name = DB_NAME()
PRINT #Cmd
EXEC(#Cmd)
OR
USE [DatabaseName]
GO
-- Option #1
EXEC sp_changedbowner 'sa'
GO
-- OR--
-- Option #2
ALTER AUTHORIZATION ON DATABASE::[DatabaseName] TO [sa]
GO
I do not know the reason why this error is occurred. However, the solution was found. Thanks to this great article!. It was necessary to recreate user in newly created database.
The whole code looks likes this:
exec sp_configure 'show advanced options', 1;
RECONFIGURE;
exec sp_configure 'clr enabled', 1;
RECONFIGURE;
use FooDatabase;
DECLARE #user NVARCHAR(max);
SELECT #user = SL.Name
FROM master..sysdatabases SD
JOIN master..syslogins SL ON SD.SID = SL.SID
WHERE SD.Name = DB_NAME()
IF ((SELECT 1 FROM sys.database_principals WHERE name = #user) = 1)
BEGIN
EXEC sp_dropuser #user
END
DECLARE #Command VARCHAR(MAX) = 'ALTER AUTHORIZATION ON DATABASE::[<<DatabaseName>>] TO
[<<LoginName>>]'
SELECT #Command = REPLACE(REPLACE(#Command
, '<<DatabaseName>>', SD.Name)
, '<<LoginName>>', SL.Name)
FROM master..sysdatabases SD
JOIN master..syslogins SL ON SD.SID = SL.SID
WHERE SD.Name = 'FooDatabase'
EXEC(#Command)
create assembly osm2mssqlSqlExtension FROM 0x4D5A900 /* some numbers more here ...*/
300000 WITH PERMISSION_SET = UNSAFE;
/* The other code is omitted for the brevity */
I am trying to create a trigger with SqlCommand and I am getting the error:
Incorrect syntax near the keyword 'trigger'
When I copy the same query in SQL Server it is executing successfully.
Here is how the SQL command looks like.
command.CommandText = "CREATE TRIGGER tr_Korisnik" + korisnik.KorisnikID + "_FakturaStavka_ForInsert " +
"on Korisnik"+korisnik.KorisnikID+"_FakturaStavka " +
"FOR INSERT " +
"AS " +
"BEGIN " +
"DECLARE #ID int " +
"DECLARE #FakturaID int " +
"DECLARE #StavkaBr int " +
"SET #ID = (SELECT DokumentID from inserted) " +
"SET #FakturaID = (SELECT FakturaID from inserted) " +
"UPDATE Korisnik"+korisnik.KorisnikID+"_Fakturi SET BrStavki = BrStavki+1 WHERE DokumentID = #FakturaID " +
"SET #StavkaBr = (SELECT Korisnik"+korisnik.KorisnikID+"_Fakturi.BrStavki FROM Korisnik"+korisnik.KorisnikID+"_Fakturi WHERE DokumentID = #FakturaID) " +
"UPDATE Korisnik"+korisnik.KorisnikID+"_FakturaStavka SET StavkaBroj = #StavkaBr WHERE DokumentID = #ID END";
command.ExecuteNonQuery();
Also, above that I have SQLCommands for CREATE TABLE and they work properly.
I tried USE [databasename] before CREATE TRIGGER, still nothing.
I removed the concatenations +"korisnik.KorisnikID" and made clean names, still can't execute it.
The documentation for ExecuteNonQuery states that
You can use the ExecuteNonQuery to perform catalog operations (for example, querying the structure of a database or creating database objects such as tables), or to change the data in a database without using a DataSet by executing UPDATE, INSERT, or DELETE statements.
Which sort of confirms my suspicions: you can't create a trigger this way.
If you want to create a trigger in code, use a CLR Trigger.
You can create a stored procedure in your database to create a trigger then pass the correct parameters. Call the stored procedure with ado.net and pass params.
Like already mentioned, a trigger should be created at design time.
Nevertheless, as follows it is possible with ExecuteNonQuery.
Use the EXEC statement and the stored procedure sp_executesql:
cmd.CommandText = "EXEC sp_executeSQL N'CREATE TRIGGER myTrigger ON myTable...'";
cmd.ExecuteNonQuery();
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.
I have a connection to a database with right to another. I want to call a procedure on the other database which has a user table data type parameter. But the user table data type isn't found whatever I try.
I tried using database name in front of [dbo].[myType] but it's not a valid syntax.
I tried creating the same type in the current database
I tried creating the same type in the model database
I tried appending "USE MyOtherDatabase" at the top of my SqlCommand.Text
Everything failed (I'm really abashed the "USE ..." approach failed).
How can I do that?
Sample of code:
// While connected to MyOtherDatabase
CREATE TYPE dbo.typeClubMembersVersion AS TABLE (
ID INT
, UNIQUE(ID)
, [version] INT
)
GO
CREATE PROCEDURE dbo.spCheckCMembersMods (
#pVersions AS dbo.typeClubMembersVersion READONLY
, #pWhoID AS BIGINT
)
AS
BEGIN
[...]
END
SqlCommand com = new SqlConnection(functions.ConnectionString).CreateCommand();
com.CommandText = #"
// While connected to CurrentDatabase
USE MyOtherDatabase
DECLARE #tbl AS dbo.typeClubMembersVersion
BEGIN TRANSACTION
UPDATE dbo.tClubMembers
SET
Title = #Title
OUTPUT inserted.ID, deleted.[version] INTO #tbl (ID, [version])
WHERE IdMember = #IdMember
EXEC dbo.spCheckCMembersMods #tbl, #whoID
COMMIT
";
com.Parameters.Add("#Title", SqlDbType.NVarChar, 20).Value = this.Title;
com.Parameters.Add("#IdMember", SqlDbType.BigInt).Value = this.Id;
com.Parameters.Add("#whoID", SqlDbType.BigInt).Value = (object)whoID ?? DBNull.Value;
com.Connection.Open();
try
{
com.ExecuteNonQuery();
}
catch (Exception exe)
{
throw exe;
}
finally
{
com.Connection.Close();
}
First, what you are calling "Schemas" are actually "Databases" in SQL Server. The "dbo." in your object names is a "Schema" in SQL Server. The "USE.." command only works on Databases.
Secondly, you cannot reference or use Types from another database, it has to be defined in the same database(s) that it is used in. Types can be in other SQL Server schemas, but not in other Databases, which is what you are actually trying to do here.
OK, as you noted, your Type is defined in [myOtherDatbase] so why doesn't it work? Probably because the USE.. and SQL command strings do not work the way you might think. Whenever you pass a string like this to SQL Server and try to execute it:
com.CommandText = #"
// While connected to CurrentDatabase
USE MyOtherDatabase
DECLARE #tbl AS dbo.typeClubMembersVersion
BEGIN TRANSACTION
UPDATE dbo.tClubMembers
SET
Title = #Title
OUTPUT inserted.ID, deleted.[version] INTO #tbl (ID, [version])
WHERE IdMember = #IdMember
EXEC dbo.spCheckCMembersMods #tbl, #whoID
COMMIT
";
SQL Server will first compile the entire string and then try to execute it. This means that all of the commands are compiled first before any of them are executed. And that means that your DECLARE #tbl and UPDATE.. commands are compiled before the USE command is executed. So when they are compiled you are still in the previous database where the Type has not been defined. This is what leads to your syntax errors (which are coming from the compiler, not from their execution).
There are three possible solutions:
Define the Type in currentDatabase also (I am pretty sure that this works, but not 100%).
Reconnect with a connection string that specifies "Initial Catalog=MyOtherDatabase".
Re-execute everything after your USE command with Dynamic SQL.
Of these I would recommend #2.
Silly me, I just realized that there is another option:
First execute just the USE command by itself,
then, execute the rest of the SQL commands on the same connection.
Of course this will leave you in [MyOtherDatabase], so you may want to end this by executing another USE command back to your original database.
It's been such a very long time since I had to use SqlConnection.ChangeDatabase I fergot about it. Until now I've always been able to use "fully named objects" to make my databases interract with each other.
Since I'm currently stuck I'll use it but I hope somebody tells me a way that don't force me to let go the current database connection.
SqlCommand com = new SqlConnection(functions.ConnectionString).CreateCommand();
com.CommandText = #"
DECLARE #tbl AS dbo.typeClubMembersVersion
BEGIN TRANSACTION
UPDATE dbo.tClubMembers
SET
Title = #Title
OUTPUT inserted.ID, deleted.[version] INTO #tbl (ID, [version])
WHERE IdMember = #IdMember
EXEC dbo.spCheckCMembersMods #tbl, #whoID
COMMIT
";
com.Parameters.Add("#Title", SqlDbType.NVarChar, 20).Value = this.Title;
com.Parameters.Add("#IdMember", SqlDbType.BigInt).Value = this.Id;
com.Parameters.Add("#whoID", SqlDbType.BigInt).Value = (object)whoID ?? DBNull.Value;
com.Connection.Open();
try
{
com.Connection.ChangeDatabase("MyOtherDatabase");
com.ExecuteNonQuery();
}
catch (Exception exe)
{
throw exe;
}
finally
{
com.Connection.Close();
}
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;