How can i use master db in stored procedures - sql

I'm trying to create restore db proc. I encountered a problem because i cannot use the command 'use master'. I have try with dynamic SQL but there is no result:
My code:
alter proc dbo.RestoreDB
(
#location as varchar(4000)
)
as
begin
declare #setMasterDb as varchar(400) = 'use master'
exec (#setMasterDb);
ALTER DATABASE [testDb] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
RESTORE DATABASE [testDb] FROM DISK = #location WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 5
ALTER DATABASE [testDb] SET MULTI_USER
end
GO
Is there workaround?

Switching database contexts (USE someDBName) in the middle of a Stored Proc (or function) is not allowed. Also, you do not need to be pointed at the master db context to run a BACKUP/RESTORE, you can be pointed to almost any DB. To fix your issue, just remove your DB switching and point your query window any DB except the one you want to restore.
EDIT: Updated to point to any DB EXCEPT the one you're trying to restore. Thanks to #DMason for that comment.

Related

I've generated an sql file full of inserts but can't find any documentation of executing this script from a stored procedure

I'm creating a stored procedure that will delete all the data in my database and then insert the data from my sql file. The reason I am using the delete and insert instead of a restore is because a restore requires that no one is connected to the database where as deleting and inserting allows people to still be connected.
Stored Procedure:
CREATE PROCEDURE DropAndRestore
-- Add the parameters for the stored procedure here
#filepath nvarchar(200)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
Exec sp_MSFOREACHTABLE 'delete from ?
RESTORE DATABASE [landofbeds] -- These lines are what needs to be replaced
FROM DISK = #FilePath --
END
GO
The reason I am using the delete and insert instead of a restore is
because a restore requires that no one is connected to the database
where as deleting and inserting allows people to still be connected
If all you need is minimum downtime you can restore your database in db_copy. Then drop your db and rename db_copy to db.
Yes you should disconnect all the users to be able to drop your db, but it will take minimum time, while if you delete your data the table will still be unavailable for the whole duration of the delete, and as delete is always fully logged your users will wait.
To launch your script you can use xp_cmdshell that calls sqlcmd with -i but it's not a good idea. You have no control on your script execution and if something goes wrong you will have even more downtime for your users.
Does your tables have FK defined?
Exec sp_MSFOREACHTABLE 'delete from ?
will try to delete everything in order it decides and you may end up with errors when you try to delete rows that are referenced in other tables.
To execute your sql file from Stored procedure .. you can use xp_cmdshell. See steps below
First Create a Batch File (C:\testApps\test.bat) and execute your sql file from there..
e.g.
osql -S TestSQlServer -E -I C:\testApps\test.sql > C:\testApps\tlog.txt
Then add this line to your Calling Stored procedure
exec xp_cmdshell 'C:\testApps\test.bat'
Execute your procedure
**Please note you will need to enable xp_cmdshell
You can use bulk insert like this:
BULK INSERT landofbeds.dbo.SalesOrderDetail
FROM '\\computer\share\folder\neworders.txt'

restore sql server from .bak file Exclusive access could not be obtained

I am trying to restore my sql using bak file
I am getting error
Exclusive access could not be obtained because the database is in use
I tried
USE [master]
ALTER DATABASE myDB
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
and run the query
USE [master] RESTORE DATABASE myDB
FROM DISK = 'C:\MyDatabase.bak' WITH FILE = 1, NOUNLOAD, STATS = 10
I also tried from restore wizard with same result.
Exclusive access could not be obtained because the database is in use.
Method 1
declare #sql as varchar(20), #spid as int
select #spid = min(spid) from master..sysprocesses where dbid = db_id('<database_name>') and spid != ##spid
while (#spid is not null)
begin
print 'Killing process ' + cast(#spid as varchar) + ' ...'
set #sql = 'kill ' + cast(#spid as varchar)
exec (#sql)
select
#spid = min(spid)
from
master..sysprocesses
where
dbid = db_id('<database_name>')
and spid != ##spid
end
print 'Process completed...'
Method 2
alter database database_name
set offline with rollback immediate
alter database database_name
set online
go
Don't need to write any query to solve this problem.
I had the same problem several times and solve it by this method:
when you are restoring database
Go to Option tab in Restore database window
Check (Overwrite the existing database (WITH REPLACE))
Check (Close existing connections to destination database)
Then click OK
Restore database is starting...
Anyone that has had the issues listed above, and none of the advice works.. Just turn off the Taillog backup under 'options'..
Setting (or leaving) this option on will attempt to take a tail-log of the source database itself (even if your source for the restore is just a file). So if the source database is in use (which if you are doing a copy of a production DB will normally be the case) then the restore fails.
I had this issue when I was trying to restore a production backup to a dev server that already had the database there. I wanted to restore as a copy, which I did by changing the target database name, but the issue was actually with the files. By default, it was trying to overwrite the files that were already there. I fixed the issue by checking the "Relocate all files to folder" in the "Files" page of the restore dialog and choosing a new directory so there wouldn't be file collisions.
None of the above solution did not work for me.
After many trials and errors, I stopped SQL Server Browser and then the restore completed successfully

Restore database from backup file, where file name matches reg expression

I am doing the below using SQL Server / T-SQL :
RESTORE DATABASE UAT
FROM DISK = 'E:\Databases\backup\MY_LIVE_20120720_070001.bak'
WITH REPLACE
But I want to be able to use a file location that ignores the numbers in the file name (which represent the date) in my backup file. There will only ever be one 'MY_LIVE_****.bak' but its number string will change each day.
The goal is to restore my UAT instance from live each week, using the latest backup - of which there will be only file matching that string prefix, but the numbers/date will change each week.
You can use xp_cmdshell to do a dir for your file. Note, however, that xp_cmdshell is normally disabled for good reasons. Given this is UAT, that may not be an issue.
See here for more http://www.sqlusa.com/bestpractices2005/dir/
ALTER DATABASE UAT
SET SINGLE_USER WITH
ROLLBACK IMMEDIATE
declare #fileName varchar(56);
SELECT #filename = physical_device_name
FROM msdb.dbo.backupmediafamily
WHERE media_set_id =(
SELECT TOP 1 media_set_id
FROM msdb.dbo.backupset
WHERE database_name='MY_LIVE'
ORDER BY backup_start_date DESC)
ALTER DATABASE UAT
SET SINGLE_USER WITH
ROLLBACK IMMEDIATE
----Restore Database
RESTORE DATABASE UAT
FROM DISK = #fileName
WITH REPLACE
/*If there is no error in statement before database will be in multiuser
mode.
If error occurs please execute following command it will convert
database in multi user.*/
ALTER DATABASE ASLA_DEV SET MULTI_USER
GO

How to pass a database name as a parameter in SQL Server

USE master
GO
DECLARE #DbName nvarchar(MAX)
SET #DbName = N'DataBase'
ALTER DATABASE #DbName
SET SINGLE_USER WITH ROLLBACK IMMEDIATE
ALTER DATABASE #DbName SET OFFLINE WITH NO_WAIT
GO
ALTER DATABASE #DbName SET ONLINE
GO
ALTER DATABASE #DbName
SET MULTI_USER
GO
I know i can use EXEC but it's a bit ugly....
It is impossible to use DB name from variable.
Use Dynamic Querying, even if it is ugly.
You can't use the database name in a variable.
You have several options:
Different DML scripts for each DB
Dynamic SQL
Firstly, you can't parameterise DDL statements like this. Secondly, GO is a batch terminator and parameters won't be available after this.
I don't recall if MSSqlServer allows the same flexibility as Oracle and MySQL, but in those you can set the default database for each connection. If the queries and statements do not specify a database (use (dbname)), it uses the default. Perhaps that is sufficient parametrization for your purposes?

Drop database only if Backup is successful

This might be an easy one for some one but I haven't found a simple solution yet.
I'm automating a larger process at the moment, and one step is to back up then drop the database, before recreating it from scratch.
I've got a script that will do the back up and drop as follows:
Use [Master]
BACKUP DATABASE [databaseName]
TO DISK='D:\Backup\databaseName\20100122.bak'
ALTER DATABASE [databaseName]
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE
DROP DATABASE [databaseName]
but I'm worried that the DROP will happen even if the BACKUP fails.
How can I change the script so if the BACKUP fails, the DROP won't happen?
Thanks in advance!
If your SQL Server version is 2005 or greater, you can wrap your statements with a try catch. If the backup fails, it will jump to the catch without dropping the database...
Use [Master]
BEGIN TRY
BACKUP DATABASE [databaseName]
TO DISK='D:\Backup\databaseName\20100122.bak'
ALTER DATABASE [databaseName]
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE
DROP DATABASE [databaseName]
END TRY
BEGIN CATCH
PRINT 'Unable to backup and drop database'
END CATCH
You can catch any error codes that occur with the SQL server error variable as follows. A zero indicates no error occurred. Note that the value is set every time a T-SQL statement is executed, so you need to catch it as soon as you have backed up:
USE [Master]
DECLARE #errorCode int
BACKUP DATABASE [databaseName]
TO DISK='D:\Backup\databaseName\20100122.bak'
SET #errorCode = ##ERROR
IF (#errorCode = 0)
BEGIN
ALTER DATABASE [databaseName]
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE
DROP DATABASE [databaseName]
END
This is the simplest way I can think of, as well as allowing you to catch known error codes and handle them differently if you need to. SELECT * FROM master.sys.messages gives you a list of all known error codes and messages if you want to take it further.