I'm using following code to restore databases,
void Restore(string ConnectionString, string DatabaseFullPath, string backUpPath)
{
string sRestore =
"USE [master] RESTORE DATABASE [" + DatabaseFullPath + "] FROM DISK = N'" + backUpPath + "' WITH FILE = 1, NOUNLOAD, STATS = 10";
using (SqlConnection con = new SqlConnection(ConnectionString))
{
con.Open();
SqlCommand cmdBackUp = new SqlCommand(sRestore, con);
cmdBackUp.ExecuteNonQuery();
}
}
but I receive below exception
"Exclusive access could not be obtained because the database is in use.
RESTORE DATABASE is terminating abnormally.
Changed database context to 'master'."
How can I fix it ?
A restore can only happen if the database does not have any connections to it (besides yours). The easy way on a MS SQL Server to kick all users off is:
ALTER DATABASE [MyDB] SET Single_User WITH Rollback Immediate
GO
Now, you can perform your restore with impunity. Make sure you set it back to Multi-user mode when you're done with the restore:
ALTER DATABASE [MyDB] SET Multi_User
GO
Thus I've written the below method to restore my database,
Am I in right way ?
void Restore(string ConnectionString, string DatabaseFullPath, string backUpPath)
{
using (SqlConnection con = new SqlConnection(ConnectionString))
{
con.Open();
string UseMaster = "USE master";
SqlCommand UseMasterCommand = new SqlCommand(UseMaster, con);
UseMasterCommand.ExecuteNonQuery();
string Alter1 = #"ALTER DATABASE [" + DatabaseFullPath + "] SET Single_User WITH Rollback Immediate";
SqlCommand Alter1Cmd = new SqlCommand(Alter1, con);
Alter1Cmd.ExecuteNonQuery();
string Restore = #"RESTORE DATABASE [" + DatabaseFullPath + "] FROM DISK = N'" + backUpPath + #"' WITH FILE = 1, NOUNLOAD, STATS = 10";
SqlCommand RestoreCmd = new SqlCommand(Restore, con);
RestoreCmd.ExecuteNonQuery();
string Alter2 = #"ALTER DATABASE [" + DatabaseFullPath + "] SET Multi_User";
SqlCommand Alter2Cmd = new SqlCommand(Alter2, con);
Alter2Cmd.ExecuteNonQuery();
labelReport.Text = "Successful";
}
}
The best approach
Alter Database <Db_Name> SET [SINGLE_USER | RESTRICTED_USER]
With ROLLBACK [IMMEDIATE | AFTER 30]
go
--do your job that needs exclusive access
go
--Back to normal mode
Alter Database <Db_Name> SET MULTI_USER
WITH ROLLBACK IMMEDIATE - this option doesn't wait for transactions
to complete it just begins rolling back all open transactions
WITH ROLLBACK AFTER nnn - this option will rollback all open
transactions after waiting nnn seconds for the open transactions to
complete. In our example we are specifying that the process should
wait 30 seconds before rolling back any open transactions.
When RESTRICTED_USER is specified, only members of the db_owner,
dbcreator, or sysadmin roles can use the database. MULTI_USER returns
the database to its normal operating state.
2nd way :using ssms 2008 R2 we can do the same thing
right-click database property
go to options -> last section with header of state
change Restrict Access to SINGLE_USER
answer yes to this helpful question which shows that this kind of action will close all other connections and i guess it is the only thing we are looking for here to by pass the error
To change the database properties, SQL Server must close all other connections to the database. Are you sure you want to change the properties and close all other connections? yes or no
restore your database
do step 1-4 changing Restrict Access back to MULTI_USER
3rd way : the following commands will also close all connections too.
ALTER DATABASE [DbName] SET OFFLINE
go
ALTER DATABASE [DbName] SET ONLINE
now database is ready for restore
More (mssqltips :Getting exclusive access to restore SQL Server databases)
You can use the method on SMO SqlServer object to kiil all processes on a specified database before performing a restore:
sqlServer.KillAllProcesses("databaseName");
The reason for this issue is self-evident (connections to the database currently open/active), but use the following (google it too so you understand it) and it'll be fine:
Alter Database YOURDB
SET SINGLE_USER With ROLLBACK IMMEDIATE
GO
Obviously, replace YOURDDB with the name of your database and run that against the master DB.
Oh, and just incase, if you get it 'stuck' in single user mode, this will undo it:
Alter Database YOURDB
SET MULTI_USER With ROLLBACK IMMEDIATE
GO
Hope this helps.
EDIT:
You can also follow this, to see where the connections are from, and other information:
I tested this while having services
running that would reconnect to the
database. I found you had to set to
Single User Mode, then run sp_who2 to
see where the one connection was
coming from, and note the SPID. You
can run the kill command for that SPID
and the restore in the same
transaction, and it should go through.
Here is the sequence I used:
USE MASTER ALTER DATABASE DATABASENAME
SET SINGLE_USER WITH ROLLBACK
IMMEDIATE GO
-This will make it so only one connection to the database can be
made.
-Run the following command to see where any recurring connections to
database are coming from.
EXEC SP_WHO2
-Check this list, looking under the DBName column. If the database is
listed, check the ProgramName, and
HostName column to see who is
attempting to connect.
-If it is not a service, or other application that would automatically
reconnect which can be shut down, note
the number in the SPID column to kill
the connection, and immediately begin
the backup. Replace SPID below with
just the number.
KILL SPID RESTORE DATABASE
DATABASENAME FROM DISK =
'X:\PATHTO\BACKUP.BAK' GO
-If this completes successfully, we can set the newly restored database
back to multi user mode.
ALTER DATABASE DATABASENAME SET
MULTI_USER WITH ROLLBACK IMMEDIATE GO
only one connection to the database can be made. -Run the following command to see where any recurring connections to database are coming from.
EXEC SP_WHO2
Check this list, looking under the DBName column. If the database is listed, check the ProgramName, and HostName column to see who is attempting to connect.
If it is not a service, or other application that would automatically reconnect which can be shut down, note the number in the SPID column to kill the connection, and immediately begin the backup. Replace SPID below with just the number.
KILL SPID RESTORE DATABASE DATABASENAME FROM DISK = 'X:\PATHTO\BACKUP.BAK' GO
If this completes successfully, we can set the newly restored database back to multi user mode.
ALTER DATABASE DATABASENAME SET MULTI_USER WITH ROLLBACK IMMEDIATE GO
Related
I am working on a program in c# where I have a layout as shown in the image below:
The purpose of the program is to perform data archiving in SQL server. If I choose "Create Tables", it will generate new tables into my database ( should generate about 40 tables in order) which has similar table structure (columns,constraint,triggers,etc) as original tables in the same database as well. How this works is I'll execute the SQL scripts in c# and call them (all 40 scripts) to create tables.
Right now, I added another button "Transfer data" where it will select specfic data(based on date) in old data and transfer them into the new tables I created. I will use the query Insert Into....SELECT from to transfer data.
My question is should I create sql scripts for transferring data and execute them in c# or just put the SQL queries inside my c# code ?
If I go with SQL scripts, should I split them into 40 scripts as well or place all the queries inside 1 script? I know it will be tedious if i put everything in one script as if an error occurs, it's hard to trace the source of the problem.
Below is a sample of how the sql query looks like :
SET IDENTITY_INSERT Kiosk_Log_New ON
INSERT INTO Kiosk_Log_New(LOGID,
logAPPID,
logLOGTID,
logGUID,
logOriginator,
logReference,
logAssemblyName,
logFunctionName,
logMessage,
logException,
CreatedBy,
CreatedDate)
SELECT LOGID,
logAPPID,
logLOGTID,
logGUID,
logOriginator,
logReference,
logAssemblyName,
logFunctionName,
logMessage,
logException,
CreatedBy,
CreatedDate FROM Kiosk_Log
WHERE CreatedDate BETWEEN '2015-01-01' AND GETDATE()
EDIT: Since many suggested stored procedure is the best option, this would be my create tables script:
string constr = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
/* open sql connection to execute SQL script: PromotionEvent_New */
try
{
using (SqlConnection con = new SqlConnection(constr))
{
con.Open();
FileInfo file = new FileInfo("C:\\Users\\88106221\\Documents\\SQL Server Management Studio\\PromotionEvent_New.sql");
string script = file.OpenText().ReadToEnd();
Server server = new Server(new ServerConnection(con));
server.ConnectionContext.ExecuteNonQuery(script);
Display("PromotionEvent_New table has been created successfully");
con.Close();
}
}
catch(Exception ex)
{
textBox1.AppendText(string.Format("{0}", Environment.NewLine));
textBox1.AppendText(string.Format("{0} MainPage_Load() exception - {1}{2}", _strThisAppName, ex.Message, Environment.NewLine));
Display(ex.Message + "PromotionEvent_New could not be created");
textBox1.AppendText(string.Format("{0}", Environment.NewLine));
Debug.WriteLine(string.Format("{0} MainPage_Load() exception - {1}", _strThisAppName, ex.Message));
}
It's best to use a stored procedure with a transaction to execute all your INSERT queries.
It's not advisable to submit queries from your C# code as explained in last post by John Ephraim Tugado due to a number of reasons; the most important reasons being,
easier maintenance of INSERT queries
minimal bandwidth consumption between web server and database server
Sending long queries strings from C# code will consume more bandwidth between web server and database server and could slow the database response in a high traffic scenario.
You can execute the following T-SQL code against your database to create a stored procedure for transferring/archiving data to archived tables. This procedure makes sure that all your INSERTS are executed within a transaction, that ensures you don't end up with orphaned tables and unnecessary headaches down the road.
Stored Procedure for transferring data
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Lord Cookie
-- Create date: 11/01/2017
-- Description: Transfers data to existing archived tables
-- =============================================
CREATE PROCEDURE dbo.ArchiveData
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
--use transaction when inserting data else you may end up with orphaned data and hard to debug issues later on
BEGIN TRANSACTION
--add your INSERT queries one after the other below
SET IDENTITY_INSERT Kiosk_Log_New ON
INSERT INTO Kiosk_Log_New (LOGID,
logAPPID,
logLOGTID,
logGUID,
logOriginator,
logReference,
logAssemblyName,
logFunctionName,
logMessage,
logException,
CreatedBy,
CreatedDate)
SELECT
LOGID
,logAPPID
,logLOGTID
,logGUID
,logOriginator
,logReference
,logAssemblyName
,logFunctionName
,logMessage
,logException
,CreatedBy
,CreatedDate
FROM Kiosk_Log
WHERE CreatedDate BETWEEN '2015-01-01' AND GETDATE()
--add more of your insert queries below
-- finally commit transaction
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE #errorDetails NVARCHAR(MAX);
set #errorDetails = 'Error ' + CONVERT(VARCHAR(50), ERROR_NUMBER()) +
', Severity ' + CONVERT(VARCHAR(5), ERROR_SEVERITY()) +
', State ' + CONVERT(VARCHAR(5), ERROR_STATE()) +
', Line ' + CONVERT(VARCHAR(5), ERROR_LINE());
--roll back the transaction
IF XACT_STATE() <> 0
BEGIN
ROLLBACK TRANSACTION
END
--you can log the above error message and/or re-throw the error so your C# code will see an error
--but do this only after rolling back
END CATCH;
END
GO
You can then call the above stored procedure using C# as shown in sample code below.
Call above stored procedure using C#
using(SqlConnection sqlConn = new SqlConnection("Your database Connection String")) {
using(SqlCommand cmd = new SqlCommand()) {
cmd.CommandText = "dbo.ArchiveData";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = sqlConn;
sqlConn.Open();
cmd.ExecuteNonQuery();
}
}
Depending on the table naming and design, I would suggest creating a script to create a stored procedure for this that would generate one for each of your tables. I'm no expert in scripting but it is the same with the script that generates an audit trail for each of your tables or at least the ones you defined in the script.
Hard-coding this inside your c# application is a big NO as there is the possibility of database changes. We would want our app to be flexible to change with the least amount of effort.
If generating the script to create a stored procedure is hard for you, I would still recommend manually creating stored procedures for this task.
I'm trying to restore a database from a .BAK file using C# and SMO. This is my code.
public static void RestoreDatabase()
{
string dbConnString = Configuration.DatabaseConnectionString;
ServerConnection connection = new ServerConnection(#"dbserver\sqlexpress", "user", "password");
Server smoServer = new Server(connection);
Restore rstDatabase = new Restore();
rstDatabase.Action = RestoreActionType.Database;
rstDatabase.Database = "AppDb";
BackupDeviceItem bkpDevice = new BackupDeviceItem(#"TestData\db-backup.bak", DeviceType.File);
rstDatabase.Devices.Add(bkpDevice);
rstDatabase.ReplaceDatabase = true;
// Kill all processes
smoServer.KillAllProcesses(rstDatabase.Database);
// Set single-user mode
Database db = smoServer.Databases[rstDatabase.Database];
db.DatabaseOptions.UserAccess = DatabaseUserAccess.Single;
db.Alter(TerminationClause.RollbackTransactionsImmediately);
rstDatabase.SqlRestore(smoServer);
}
However when I try to run this method I get the following (error) message when it attempts to kill all processes:
Cannot use KILL to kill your own process.
I would be very grateful if someone could help solve this issue.
Simply not call KillAllProcesses. RollbackTransactionsImmediately is enough. It kills all sessions that are in that database right now.
KillAllProcesses does not help you anyway because right after it is done killing sessions a new one could appear.
It happens to me all the time when I am trying to restore a database and there is an active connection somewhere and database will not restore until there are no more active connections to the database.
Usually what I do is, I execute the following set of commands to disconnect everyone and then restore the database .
ALTER DATABASE [DatabaseName]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
-- at this point all the users will be disconnected and the database is in
-- single-user mode
Use [DatabaseName] --<-- Grab that single connection
GO
Use [master] --<-- Disconnect from database and connect to Master DB for restore
GO
RESTORE DATABASE [DatabaseName] --<-- Finally restore database tara
FROM DISK .........
GO
after connecting to database in C#
string MyConString2 = "SERVER=localhost;" + "user id=mytest;" + "DATABASE=clusters;" + "PASSWORD=mypass;";
I have an algorithm which I need to after each run of algorithm that will fill the database, drop the database "clusters" of mysql manually and again connect to the empty database and run it again,gaining new data in tables
I want to make it automatically how can I drop or empty my database if exists in C# and then run my algorithm?
Here is example code that works and I think this is what you are talking about, if not, feel free to correct me.
using (var connection = new MySqlConnection("server=localhost;user=root;password="))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "drop schema if exists clusters";
command.ExecuteNonQuery();
command = connection.CreateCommand();
command.CommandText = "create schema clusters";
command.ExecuteNonQuery();
}
Prepare sql query for clearing your DB and test it in f.e. MySQL workbench. Following this, just execute it as you would execute regular query against DB in C#. One way is to clear all the tables in your database by using TRUNCATE statement and the second way is to DROP DATABASE and recreate it.
In my C# application, I do the following:
using (SqlConnection connection = new SqlConnection(connectionstr))
{
connection.Open();
SqlCommand com1 = new SqlCommand("insert into Anfangstierbestand(bestand, jahr, loginid) VALUES (" + bestand + ", " + jahr + ", " + loginid + ");", connection);
SqlCommand com2 = new SqlCommand("select * from Anfangstierbestand;", connection);
com1.Connection = connection;
int ferksum = 0;
com1.ExecuteNonQuery();
connection.Close();
connection.Open();
SqlDataReader read = com2.ExecuteReader();
while (read.Read())
{
ferksum += Convert.ToInt32(read[2]);
}
// MessageBox.Show("Fehler beim Erstellen des Tierbestandes", "Fehler"); }
MessageBox.Show(ferksum.ToString());
}
It's a simple insert to a database. I added com2 to check, if the insert works.
Unfortunately, the the value of com2 tells me, that it works, but when I go to the Server Explorer and press Execute SQL, there are no new values.
I don´t know the reason. The code above is just an example, since a few days, no insert works anymore(before, the same code works).
Does anybody know what the reason can be?
EDIT:
C#:
string connectionstr = "Data Source=.\\SQLEXPRESS;" + "AttachDbFilename=|DataDirectory|\\Datenbank\\FarmersCalc.mdf;" + "Integrated Security=True;" + "User Instance=true;";
EDIT2:
Server Explorer:
Data Source=.\SQLEXPRESS;AttachDbFilename="C:\Users\user\Desktop\Farmer´s Calc\Programmierung\WPF\Finanz_WPF\Finanz_WPF\Datenbank\FarmersCalc.mdf";Integrated Security=True;User Instance=True
EDIT3:
Here are the columns:
id int,
jahr int,
anzahl int,
loginid int
EDIT4:
Is it possible that it´s a problem that I opened my project with expression blend? Normally, I work with VS 2010...
EDIT5:
Even because I can not answer my question(<100 reputations) I write it here:
Shame on me. Some of you were right, it was the wrong database-file. But I´m still wondering, why this happened, because I did not change anything since a few days and before this, it worked!
Thanks to all for helping!
You're using user instances, why? Isn't it possible that the instance you're connecting to in Server Explorer is not the same as the instance where your app is inserting data? What happens when you select data from within the app, does it reflect your insert(s)? It seems to me based on your connection strings that these are two completely different MDF files - they have the same name but they are in different locations.
Is the issue implicit transactions? Do you need to issue a commit of the SQL Insert? If com2 tells you that it's there then it may only see it since it's in the context of the current SQL transaction.
You should share your connection string for both Server Explorer and your SqlConnection (connectionstr). Ensure that you setup your Server Explorer connection with User Instance=true;.
As a diagnostic,
connection.Open();
int n = com1.ExecuteNonQuery();
// log or show 'n' , the nr of rows affected
Edit, after seeing the connectionstrings:
local db files in |DataDirectory|\ are very prone to be overwritten by the next run or build command.
Make sure you set them to 'copy never' or 'copy if newer' in the VS Solution explorer.
Perhaps somewhere in the callstack above this code, someone has added a TransactionScope.
This would cause the connections to enroll automatically in the transaction, and the connections could see the data inserted via that not-yet-committed transaction. When the scope is exitted without calling scope.Complete, the transaction gets rolled back and no data is saved.
I am having problems with SQL Server dropping a connection after I have dropped and re-created a given database and the next time I try to execute a command against a new connection on that same database, I get:
A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.)
Here is the TCP version (If I try connecting to another server)
A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
Here are the steps to repro the problem:
Open a connection to a database and execute a sql command
Drop the database
Re-Create the database
Open a new connection to the same database and attempt to run a command against it
Result: I receive an exception
Here is the Code:
using (var conn = new System.Data.SqlClient.SqlConnection("Data Source=.;Initial Catalog=DBNAME;Integrated Security=True"))
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = "UPDATE ...";
cmd.ExecuteNonQuery();
}
string sql = "Alter Database DBNAME set single_user with rollback immediate drop database DBNAME";
var server = new Microsoft.SqlServer.Management.Smo.Server(".");
server.ConnectionContext.ExecuteNonQuery(sql);
server.ConnectionContext.Disconnect();
sql = File.ReadAllText("PathToDotSqlFile..."));
server = new Microsoft.SqlServer.Management.Smo.Server(".");
server.ConnectionContext.ExecuteNonQuery(sql);
server.ConnectionContext.Disconnect();
using (var conn = new System.Data.SqlClient.SqlConnection("Data Source=.;Initial Catalog=WER_CONFIG;Integrated Security=True"))
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = "UPDATE ...";
cmd.ExecuteNonQuery();
}
The error occurs on the line 'cmd.ExecuteNonQuery()' at the very end. It appears that even though I am creating a new connection each time I connect, sql server is keeping track of something (or possibly the ADO.net code) where the next time I ask for a connection, it gives me one that is already used or has been closed on the server side. It doesn't realize it was closed by the server (presumably because of the database it is connected to being dropped) until you try to execute another command against it.
Note that if I don't do the first step of executing the initial query, and I just drop the database, re-create it, and execute a command, I do not receive this error. I think establishing that initial connection before the database is dropped is an important part of this error.
I have also tried using an external process to drop and recreate the database like so:
ProcessStartInfo info = new ProcessStartInfo("sqlcmd.exe", " -e -E -S . -Q \"Alter Database DBNAME set single_user with rollback immediate drop database DBNAME\"");
var p = Process.Start(info);
p.WaitForExit();
info = new ProcessStartInfo("sqlcmd.exe", " -i " + PathToDotSqlFile);
p = Process.Start(info);
p.WaitForExit();
And that did not help.
Is there a way to create a new SqlConnection and ensure it is clean and not from a pool? Any other suggestions on how to solve this problem?
UPDATE: Using SqlConnection.ClearPool() did solve the problem but I chose to just edit my connection string with pooling=false which also worked.
ADO.NET automatically manages a connection pool. When you "close" a connection in your application, it is returned to the pool and kept alive, in case you request a connection with the same connection string. This could be the reason why your "new" connection is stale.
You could try to turn off this behaviour by adding pooling=false as parameter to your connection string.
Don't know about SQL2008, but this sounds like a connection pooling problem on the application side. In the old days, we added "OLEDB Services=-1" to the connection string to turn off connection pooling. There is probably a more elegant way to do this now.
edit: ADO.Net 2.0 seems to have added a ClearPool function to the SQLConnection object. (http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.clearpool(VS.80).aspx) I am very interested to know if this works.