Copy SQL Server MDF and LDF files while server is in use - sql

I am using the following code to copy files from one folder to another...
Public Shared Sub CopyFlashScriptFile(ByVal SourceDirectory As String, ByVal DestinationDirectory As String)
Try
Dim f() As String = Directory.GetFiles(SourceDirectory)
For i As Integer = 0 To UBound(f)
File.Copy(f(i), DestinationDirectory & "\" & System.IO.Path.GetFileName(f(i)),True)
Next
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
The files I am copying are database files, .mdf and .ldf. Which are being used by the application. Now the problem is when I try to copy the files it throws an error
file is being used by another process
Can anyone help me with this?
Is there anyway I can programmatically stop SQL Server and copy the files, then start the server again?

To expand on my comment - I would build a .sql file with the T-SQL command to backup the database to another location, and then I can use sqlcmd from the command line in order to run the backup .sql file.
So to build the .sql file I would go through the process of backing up the database via SQL Server Management Studio. Here is a tutorial on how to do this:
http://www.serverintellect.com/support/sqlserver/database-backup-ssmse.aspx
Then, before clicking OK to perform the backup, click on the "Script" button on the backup window and choose "Script Action To New Query Window". This will generate the SQL of your settings from the backup database window. Save that SQL into a file and you're done.
Next is to use sqlcmd.exe to execute the .sql file to backup the database whenver you want. There is a very good example of using sqlcmd.exe from C# code here:
http://geekswithblogs.net/thomasweller/archive/2009/09/08/automating-database-script-execution.aspx
I always prefer doing stuff like this without affecting the running SQL Server (unless it's one running on my dev machine, where I'll happily stop/start the service). You just never what might happen if you stop a production SQL Server service to copy some files. Could be very costly, so better to be safe.

Depending on which version of SQL you are using you could make use of the Microsoft.SqlServer.Management.Smo.Wmi.Service objects to Start and Stop the Service that runs the SQL instance.
After doing this you should be able to simply copy the files as needed.
For SQL Server 2008
{
//Declare and create an instance of the ManagedComputer
//object that represents the WMI Provider services.
ManagedComputer mc;
mc = new ManagedComputer();
//Iterate through each service registered with the WMI Provider.
foreach (Service svc in mc.Services)
{
Console.WriteLine(svc.Name);
}
//Reference the Microsoft SQL Server service.
Service Mysvc = mc.Services["MSSQLSERVER"];
//Stop the service if it is running and report on the status
// continuously until it has stopped.
if (Mysvc.ServiceState == ServiceState.Running) {
Mysvc.Stop();
Console.WriteLine(string.Format("{0} service state is {1}", Mysvc.Name, Mysvc.ServiceState));
while (!(string.Format("{0}", Mysvc.ServiceState) == "Stopped")) {
Console.WriteLine(string.Format("{0}", Mysvc.ServiceState));
Mysvc.Refresh();
}
Console.WriteLine(string.Format("{0} service state is {1}", Mysvc.Name, Mysvc.ServiceState));
//Start the service and report on the status continuously
//until it has started.
Mysvc.Start();
while (!(string.Format("{0}", Mysvc.ServiceState) == "Running")) {
Console.WriteLine(string.Format("{0}", Mysvc.ServiceState));
Mysvc.Refresh();
}
Console.WriteLine(string.Format("{0} service state is {1}", Mysvc.Name, Mysvc.ServiceState));
Console.ReadLine();
}
else {
Console.WriteLine("SQL Server service is not running.");
Console.ReadLine();
}
}
From msdn

I am using the .mdf file in my application...in case of a system crash or format the user is going to loose the data..so if the user copies the data(.mdf) to some other drive ..he/she can replace the new .mdf file with the old one which has all there data...correct me if i am wrong...thanks.
That's exactly what "normal" backups are for.
As you noticed yourself, you can backup a SQL Server database by simply copying the .mdf and .ldf files, but the downside is that you can only do this when the SQL Server service is not running.
And stopping the SQL Server service just to backup the database is not a good idea, because your users can't access the database while the service is stopped.
Taking a "normal" backup (usually a .bak file) can be done while the database is running, so there's no need to stop SQL Server every time you want to make a backup.
There are several ways how to do a backup:
a) Manually in SQL Server Management Studio:
see the first link in Jason Evans' answer
b) If you want to take a backup regularly (say, once a day) you need to use sqlcmd.
Jason Evans described this in his answer as well, but IMO there's an easier way - you need only two files with one line each. See How to create jobs in SQL Server Express edition.
(if you were using a full SQL Server edition and not only Express, you could set up a Maintenance Task in Management Studio instead, but that's not possible in SQL Server Express, so you have to do it manually like described above).

Related

Cannot restore database as file is being used by another process

I have developed this application for a store owner.I want to allow the owner to backup and restore database by using the application.the backup runs fine but the restore is causing an exception which says that - Operating system error 32( the process cannot access the file because it is being used by another process).Restore database is terminated abnormally.
using(var conn = new SqlConnection(ConnectionString))
{
using(SqlCommand cmd = conn.CreateCommand())
{
string datadirectory = Path.Combine(Environment.CurrentDirectory,#"Data");
string query = #"RESTORE DATABASE""{0}""FROM DISK= '{1}' WITH REPLACE";
string query = String.Format(query,backupfile,datadirectory + "\\Database.mdf");
conn.Open();
SqlCommand command = new SqlCommand(query,conn);
command.ExecuteNonQuery();
}
}
How can I solve this issue ? Thanks in advance.
You have to dispose of every SQLiteConnection, SQLiteCommand and SQLiteDataReader once you are done using it. The second command that you create isn't correctly being disposed off.
That aside, your code sample doesn't really make sense. You create a command that is never used. Then you create a second command that isn't properly disposed off.
Is it the restore-file that is blocking? Or is the database itself still running?
If it is the database that is being used, you can set the database in single-user-mode. Another option is taking the database temporarily offline and bring it online again. That should close all existing connections. Tip; with SSMS you can turn almost every command into an SQL script like the button to brink a database offline. Click on 'Script' and you get something like 'USE MASTER GO ALTER DATABASE [AdventureWorks] SET OFFLINE GO'.
wait, this is good news. it looks like the application is running, the db is online and live, so why do want to restore? backups are something you do daily/hourly... but restores you ONLY do if something goes wrong. of course you got an error. the db is live and sql service is using the files and it's good it didn't let you restore or else you would have lost a lot of data.
if all you want is to test the restore, then you need to shut down the sql service first.BUT, make sure you take a backup just before that so you restore the latest.

Verify Backup SQL Server database using SMO

I'm using Vb.net 2013 and SQL Server 2008 R2.
I have a backup database, and I want to restore with SMO. But before restoring I want to verify if this file that I want to restore is a valid SQL Server database backup and has the identical structure (tables , fields) with the database that my program uses.
I know about the Restore.ReadBackupHeader method, but I don't know what information it holds and how I can use it to test this header with the header of a correct database?
Thank you !
I do it in Powershell with smo and and backupdevise. I scan a directory for certain backupfiles and then check in the header what the name of the database is and where the database is from what server and date and so. Finally I map it to the right database and do a restore.
See microsoft ref: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.backupdevice.readbackupheader(v=sql.105).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
found this VB code
'Declaration
Public Function ReadBackupHeader As DataTable
'Usage
Dim instance As BackupDevice
Dim returnValue As DataTable
returnValue = instance.ReadBackupHeader()
here is other code:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/c8e5b271-f053-4d3e-bd39-3d2a89e55af0/version-of-sql-back-up-file?forum=sqlsmoanddmo
Here is my powershell at github code: https://github.com/patriklindstrom/Powershell-pasen/blob/master/Restore-Sql.ps1
[Microsoft.SqlServer.Management.Smo.BackupDeviceItem]$backupDevice = New-Object ("Microsoft.SqlServer.Management.Smo.BackupDeviceItem") ($f.fullname, "File")
#set param so the PercentComplete notification is updated every 10 sec
$smoRestore.Devices.Add($backupDevice)
$ErrorActionPreference = "SilentlyContinue"
#read db name, original sql server name and date from the backup file's backup header
$smoRestoreDetails = $smoRestore.ReadBackupHeader($server)
$DBNameFromBackup = $smoRestoreDetails.Rows[0]["DatabaseName"]
$DatabaseList += $DBNameFromBackup
$OriginalDBServer = $smoRestoreDetails.Rows[0]["ServerName"]
$OriginalDBBackupDate = $smoRestoreDetails.Rows[0]["BackupFinishDate"]
$smoRestore.Database = $DBNameFromBackup
I would suggesst that you write to the description field version number or key /changeset from code repo when backup is made. Then you check it when you want to restore. You can see Header info with eg RESTORE HEADERONLY FROM DISK = 'E:\SQL\big_DB.bak'. I mean a mean database can contain 1000 tables and 30.000 stored procedures. How should you check them in your restore script?

create auto sql script that runs in every hour - in c# or any other easy way

I have simple sql script:
Select * from student where score > 60
What i am trying to do is run this above script every 1 hour and getting notified on my computer in any way possibe that above condition was met. So basically i dont want to go in there and hit F5 every hour on the above statement and see if i get any result. I am hoping someone out here has something exactly for this, if you do please share the code.
You can use Sql Agent to create a job, Sql server 2008 also has mail functionality
Open SQL Management Studio and connect to your SQL Server
Expand the SQL Server Agent node (if you don't see it, use SQL configuration manager or check services and ensure that SQL Server Agent (SQLINSTANCENAME) is started)
Right click on Jobs and choose 'New Job'
You can run a SQL statement in a job. I'll let you figure out the rest of that part (it's pretty intuitive)
You may want to send your mail using xp_sendmail
Check out the SQL documentation for xp_sendmail
http://msdn.microsoft.com/en-us/library/ms189505(v=sql.105).aspx
You might need to turn the feature on (afaik it's off by default) and you need some server/machine to deliver the mail (so you might need IIS and SMTP installed if on a local machine)
Edit:
Assuming you can't access the server and want to do this on the client side, you can create a .NET framework app or windows service to do the work for you using a schedule or a timer approach:
Schedule approach:
Create a simple command line application which does the query and mails the results, and use the windows scheduler to invoke it every hour (or whatever your interval may be)
Timer approach:
Create a simple application or windows service that will run a timer thread which does the work every x number of minutes
I'd probably just go for the former. The code would be quite simple - new console app:
static void Main(string args[])
{
// No arguments needed so just do the work
using(SqlConnection conn = new SqlConnection("ConnectionString"))
{
using(SqlCommand cmd = new SqlCommand("sql query text", conn))
{
var dr = cmd.ExecuteReader();
List<myClass> results = new List<myClass>();
// Read the rows
while(dr.Read())
{
var someValue = dr.GetString(dr.GetOrdinal("ColumnName"));
// etc
// stuff these values into myClass and add to the list
results.Add(new myClass(someValue));
}
}
}
if(results.Count > 0) // Send mail
{
//Send the message.
SmtpClient client = new SmtpClient(server);
// Add credentials if the SMTP server requires them.
client.Credentials = CredentialCache.DefaultNetworkCredentials;
MailMessage message = new MailMessage(
"recipient#test.com",
"sender#test.com",
"Subject",
"Body");
// Obviously you'd have to read the rows from your list, maybe override ToString() on
// myClass and call that using a StringBuilder to build the email body and append the rows
// This may throw exceptions - maybe some error handling (in any of this code) is advisable
client.Send(message);
}
}
Disclaimer: probably none of this will compile :D
Edit 2: I'd go this way as it's much easier to debug than a windows service as you can just run it from the command line. You can also pass command line arguments so you don't need an application configuration file

SSMS 2008 not remembering connection login credentials [duplicate]

I've recently used our company's spare laptop (that has a general user set up) while mine was being repaired. I've checked the "Remember password" option in SQL Server Management Studio when logging in to the database.
I need to clear the login and password information that I have used to prevent the next person that will use the laptop from using my login names and passwords. How can I do this?
Another answer here also mentions since 2012 you can remove Remove cached login via How to remove cached server names from the Connect to Server dialog?. Just confirmed this delete in MRU list works fine in 2016 and 2017.
SQL Server Management Studio 2017 delete the file
C:\Users\%username%\AppData\Roaming\Microsoft\SQL Server Management Studio\14.0\SqlStudio.bin
SQL Server Management Studio 2016 delete the file
C:\Users\%username%\AppData\Roaming\Microsoft\SQL Server Management Studio\13.0\SqlStudio.bin
SQL Server Management Studio 2014 delete the file
C:\Users\%username%\AppData\Roaming\Microsoft\SQL Server Management Studio\12.0\SqlStudio.bin
SQL Server Management Studio 2012 delete the file
C:\Users\%username%\AppData\Roaming\Microsoft\SQL Server Management Studio\11.0\SqlStudio.bin
SQL Server Management Studio 2008 delete the file C:\Users\%username%\AppData\Roaming\Microsoft\Microsoft SQL Server\100\Tools\Shell\SqlStudio.bin
SQL Server Management Studio 2005 delete the file – same as above answer but the Vista path.
C:\Users\%username%\AppData\Roaming\Microsoft\Microsoft SQL Server\90\Tools\Shell\mru.dat
These are profile paths for Vista / 7 / 8.
EDIT:
Note, AppData is a hidden folder. You need to show hidden folders in explorer.
EDIT:
You can simply press delete from the Server / User name drop down (confirmed to be working for SSMS v18.0). Original source from https://blog.sqlauthority.com/2013/04/17/sql-server-remove-cached-login-from-ssms-connect-dialog-sql-in-sixty-seconds-049/ which mentioned that this feature is available since 2012!
This works for SQL Server Management Studio v18.0
The file "SqlStudio.bin" doesn't seem to exist any longer. Instead my settings are all stored in this file:
C:\Users\*********\AppData\Roaming\Microsoft\SQL Server Management Studio\18.0\UserSettings.xml
Open it in any Texteditor like Notepad++
ctrl+f for the username to be removed
then delete the entire <Element>.......</Element> block
that surrounds it.
EDIT:
An even easier and working solution for v18.0 (Preview 7) would be:
Go to the "Connect to Server" dialogue window:
Click the down-arrow icon marked green in the screenshot.
Use the arrow-keys on the keyboard to navigate up/down
Press the DEL key on keyboard to delete the entry.
Close the dialogue window and when you reopen it the entry will indeed be removed.
For those looking for the SSMS 2012 solution... see this answer:
Remove cached login 2012
Essentially, in 2012 you can delete the server from the server list dropdown which clears all cached logins for that server.
Works also in v17 (build 14.x).
In my scenario I only wanted to remove a specific username/password from the list which had many other saved connections I didn't want to forget. It turns out the SqlStudio.bin file others are discussing here is a .NET binary serialization of the Microsoft.SqlServer.Management.UserSettings.SqlStudio class, which can be deserialized, modified and reserialized to modify specific settings.
To accomplish removal of the specific login, I created a new C# .Net 4.6.1 console application and added a reference to the namespace which is located in the following dll: C:\Program Files (x86)\Microsoft SQL Server\130\Tools\Binn\ManagementStudio\Microsoft.SqlServer.Management.UserSettings.dll (your path may differ slightly depending on SSMS version)
From there I could easily create and modify the settings as desired:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.SqlServer.Management.UserSettings;
class Program
{
static void Main(string[] args)
{
var settingsFile = new FileInfo(#"C:\Users\%username%\AppData\Roaming\Microsoft\SQL Server Management Studio\13.0\SqlStudio.bin");
// Backup our original file just in case...
File.Copy(settingsFile.FullName, settingsFile.FullName + ".backup");
BinaryFormatter fmt = new BinaryFormatter();
SqlStudio settings = null;
using(var fs = settingsFile.Open(FileMode.Open))
{
settings = (SqlStudio)fmt.Deserialize(fs);
}
// The structure of server types / servers / connections requires us to loop
// through multiple nested collections to find the connection to be removed.
// We start here with the server types
var serverTypes = settings.SSMS.ConnectionOptions.ServerTypes;
foreach (var serverType in serverTypes)
{
foreach (var server in serverType.Value.Servers)
{
// Will store the connection for the provided server which should be removed
ServerConnectionSettings removeConn = null;
foreach (var conn in server.Connections)
{
if (conn.UserName == "adminUserThatShouldBeRemoved")
{
removeConn = conn;
break;
}
}
if (removeConn != null)
{
server.Connections.RemoveItem(removeConn);
}
}
}
using (var fs = settingsFile.Open(FileMode.Create))
{
fmt.Serialize(fs, settings);
}
}
}
There is a really simple way to do this using a more recent version of SQL Server Management Studio (I'm using 18.4)
Open the "Connect to Server" dialog
Click the "Server Name" dropdown so it opens
Press the down arrow on your keyboard to highlight a server name
Press delete on your keyboard
Login gone! No messing around with dlls or bin files.
Delete entire node "Element" (inside "Connections" tree) from XML file, used by version 18 or higher.
C:\Users\%username%\AppData\Roaming\Microsoft\SQL Server Management Studio\18.0\UserSettings.xml
As gluecks pointed out, no more SqlStudio.bin in Microsoft SQL Server Management Studio 18. I also found this UserSettings.xml in C:\Users\userName\AppData\Roaming\Microsoft\SQL Server Management Studio\18.0. But removing the <Element> containing the credential seems not working, it comes right back on the xml file, if I close and re-open it again.
Turns out, you need to close the SQL Server Management Studio first, then edit the UserSettings.xml file in your favorite editor, e.g. Visual Studio Code. I guess it's cached somewhere in SSMS besides this xml file?! And it's not on Control Panel\All Control Panel Items\Credential Manager\Windows Credentials.
For SQL Server Management Studio 2008
You need to go C:\Documents and Settings\%username%\Application
Data\Microsoft\Microsoft SQL Server\100\Tools\Shell
Delete SqlStudio.bin
Delete:
C:\Documents and Settings\%Your Username%\Application Data\Microsoft\Microsoft SQL Server\90\Tools\Shell\mru.dat"
In XP, the .mru.dat file is in C:\Documents and Settings\Name\Application Data\Microsoft\Microsoft SQL Server\90\Tools\ShellSEM
However, removing it won't do anything.
To remove the list in XP, cut the sqlstudio bin file from C:\Documents and Settings\Name\Application Data\Microsoft\Microsoft SQL Server\100\Tools\Shell and paste it on your desktop.
Try SQL
If it has worked, then delete the sqlstudio bin file from desktop.
Easy :)
Select the Login drop down arrow. Delete the users from the list

WebSharingAppDemo-CEProviderEndToEnd Queries peerProvider for NeedsScope before any files are batched to the server. This seems out of order?

I'm building an application based on the WebSharingAppDemo-CEProviderEndToEnd. When I deploy the server portion on a server, the code gives the error "The path is not valid. Check the directory for the database." during the call to NeedsScope() in the CeWebSyncService.cs file.
Obviously the server can't access the client's sdf but what is supposed to happen to make this work? The app uses batching to send the data and the batches have to be marshalled across to the temp directory but this problem is occurring before any files have been batched over. There is nothing for the server to look at to determine whether the peerProivider needs scope. What am I missing?
public bool NeedsScope()
{
Log("NeedsSchema: {0}", this.peerProvider.Connection.ConnectionString);
SqlCeSyncScopeProvisioning prov = new SqlCeSyncScopeProvisioning();
return !prov.ScopeExists(this.peerProvider.ScopeName, (SqlCeConnection)this.peerProvider.Connection);
}
I noticed that the sample was making use of a proxy to speak w/ the CE file but a provider (not a proxy) to speak w/ the sql server.
I switched it so there is a proxy to reach the SQL server and a provider to access the CE file.
That seems to work for me.
stats = synchronizationHelper.SynchronizeProviders(srcProvider, destinationProxy);
vs.
SyncOperationStatistics stats = syncHelper.SynchronizeProviders(srcProxy, destinationProvider);