Restore stored procedure - sql-server

I have a database in SQL Server 2008 R2 and I created this stored procedure for restoring databases:
CREATE PROCEDURE [dbo].[usp_DBRestore]
#DBName nvarchar(60)
,#BackName nvarchar(120)
,#OutMessage nvarchar(4000) output
--,
--#DataName varchar(60),
--#DataFileName varchar(120),
--#LogName varchar(60),
--#LogFileName varchar(120)
AS
BEGIN TRY
USE [master]
ALTER DATABASE #DBName SET SINGLE_USER WITH ROLLBACK IMMEDIATE
RESTORE DATABASE #DBName FROM
DISK = #BackName WITH
FILE = 1, NOUNLOAD,
REPLACE,
PASSWORD = 'TEST'
SET #OutMessage = 'OK';
ALTER DATABASE #DBName SET MULTI_USER WITH ROLLBACK IMMEDIATE
END TRY
BEGIN CATCH
ALTER DATABASE #DBName SET MULTI_USER WITH ROLLBACK IMMEDIATE
INSERT [dbo].[ErrorLog]
(
[UserName],
[ErrorNumber],
[ErrorSeverity],
[ErrorState],
[ErrorProcedure],
[ErrorLine],
[ErrorMessage]
)
VALUES(
CONVERT(sysname, CURRENT_USER),
ERROR_NUMBER(),
ERROR_SEVERITY(),
ERROR_STATE(),
ERROR_PROCEDURE(),
ERROR_LINE(),
ERROR_MESSAGE()
)
END CATCH
When I execute code I see this error :
a USE database statement is not allowed in a procedure, function or
trigger.
How can I solve this error?

You cannot do this in that way - you basically have two options:
stick to a stored procedure, but in that case, you have to use dynamic SQL. Your stored procedure creates a string of SQL statements, which allows it to use USE master and it allows it to dynamically set the database name etc., and then it executes that SQL statement using sp_executesql #sqlRestoreStatement. If you want to check this out, you MUST be all means read (and understand) Erland Sommarskog's seminal article The Curse and Blessings of Dynamic SQL
you can use a regular SQL script, possibly with SQLCMD placeholders (if you have SQLCMD mode enabled in your SQL Server Management Studio) and execute the restore from a regular script (which you can put into your own template folder, for instance). In that case, you'd have something like:
:setvar dbname YourDatabaseNameHere
DECLARE #FileName NVARCHAR(255)
SET #FileName = N'D:\YourBackupDirectory\SomeDatabase.bak'
RESTORE DATABASE [$(dbname)]
FROM DISK = #FileName
WITH FILE = 1,
MOVE N'YourDatabase_Data' TO N'D:\MSSQL\Data\$(dbname).mdf',
MOVE N'YourDatbase_Log' TO N'D:\MSSQL\Data\$(dbname)_Log.ldf',
NOUNLOAD, REPLACE,
STATS = 2
GO
With this setup, you can easily use the SQL script as a template and restore any kind of database using it.

You don't need the USE statement. Best is to remove Use statement and create / Alter this sp on master database itself.
If you want to take a backup execute this SP from master DB. I can not see any other way out.

You can create a linked server and have that referenced in your stored procedure.
For example. LinkedServer.database.[dbo].StoredProcedure
Check out this
How to create the linked server for SQL Server 2008 where we have the database from 2000 and 2005

Related

SQL Server Ignoring IF Statement

Every night, I backup my production server and push the backup to my dev server. My dev server then has a job that runs which first checks if the backup file exits, if so check if the database exists in dev and if so drop the database, then restore from file. This all works fine, unless the file is not yet complete due to slow transfer, etc. If the file is not completely downloaded when the job runs then the first step sees it exists and drops the database. The next step tries to restore and of course fails. The next day when the job runs I would expect that when it checks if the database exists, it would see that it does not and shouldn't attempt to drop it and just restore. However, what's happening is the job is unable to drop the database and just fails at that point. This requires manual intervention to get the database restored, which is my problem. I'm less concerned with the fact of having no database existing on the server for a day (in theory) as I can tweak the schedule further to restore sooner. What I am concerned with is why is the IF statement not working to check if the database exists and attempts to drop a database regardless? Here's the T-SQL code that I am using:
DECLARE #output INT
DECLARE #SqlPath varchar(500) = 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Backup\PROD-01_prod_backup.bak'
EXECUTE master.dbo.xp_fileexist #SqlPath, #output OUT
IF #output = 1
BEGIN
IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = '[PROD-01]')))
BEGIN
ALTER DATABASE [PROD-01] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DROP DATABASE [PROD-01]
END
RESTORE DATABASE [PROD-01] FROM DISK = #SqlPath
END
I'm not sure how this is happening, as I am unable to reproduce, but a TRY / CATCH block is an ideal solution for this case:
SET XACT_ABORT ON
GO
DECLARE #output INT
DECLARE #SqlPath varchar(500) = 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Backup\PROD-01_prod_backup.bak'
EXECUTE master.dbo.xp_fileexist #SqlPath, #output OUT
IF #output = 1
BEGIN
IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE (name = 'PROD-01')))
BEGIN TRY
ALTER DATABASE [PROD-01] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DROP DATABASE [PROD-01]
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE();
END CATCH
RESTORE DATABASE [PROD-01] FROM DISK = #SqlPath
END

The specified schema name "sys" either does not exist or you do not have permission to use it

I needed Msforeach_table stored procedure which depends upon sys.MSforeach_worker (System) stored procedure.
I am following this source code to create stored procedure MSforeach_worker
The syntax here is for dbo and not for sys so I have changed it to sys.MSforeach_worker from dbo.MSforeach_worker
When I try to create in my Databases, i get this error
The specified schema name "sys" either does not exist or you do not
have permission to use it
And when I try to create it in master db, I get
CREATE PROCEDURE permission denied in database 'master'
I am confused where should I run this script to create System stored procedure in my SQL server.
I have googled but could not find solution to my problem.
First, don't use undocumented system stored procedures. These are not supported.
Second, if these undocumented procs don't already exist, you must be using Azure SQL Database. Azure SQL Database has a significantly different architecture with regards to separation of master and user databases. Rather than trying to port the procs, I suggest you create your own proc with the functionality you need. Below is an example.
CREATE PROC dbo.usp_ForEachTable
#SQL nvarchar(MAX)
AS
DECLARE
#SQLBatch nvarchar(MAX)
, #TableName nvarchar(261);
DECLARE tables CURSOR LOCAL FAST_FORWARD FOR
SELECT QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + '.' + QUOTENAME(name)
FROM sys.tables
WHERE is_ms_shipped = 0;
OPEN tables;
WHILE 1 = 1
BEGIN
FETCH NEXT FROM tables INTO #TableName;
IF ##FETCH_STATUS = -1 BREAK;
SET #SQLBatch = REPLACE(#SQL, N'?', #TableName);
EXEC sp_executesql #SQLBatch;
END;
CLOSE tables;
DEALLOCATE tables;
GO

SQL Server 2014: FileTable Trigger w/ Stored Procedure w/ xp_cmdshell

I am using a FileTable in SQL Server 2014 and need to run an executable that parses the file name of any inserted/updated/deleted file and then in turn the executable inserts into other tables on the database the information that was parsed from the name. I do not expect the .exe to run long at all but if it runs into issues, I do not want to lock it for an extended period of time.
For instance:
CREATE PROCEDURE filename_parser
#name nvarchar(255)
AS
BEGIN
DECLARE #exe nvarchar(255)
SET #exe = 'c:\test\my.exe "' + #name + '"'
EXEC master..xp_cmdshell #exe
END
GO
If I run the stored procedure from an INSERT or UPDATE trigger, for instance:
USE [db_1]
GO
CREATE TRIGGER [dbo].[i_table_a]
ON
[dbo].[table_a]
AFTER
INSERT
AS
DECLARE #file nvarchar(255)
SELECT TOP 1
#file = name
FROM
inserted
EXEC filename_parser #name = #file
will I end up locking table_a until the executable completes? Sorry, if the answer is obvious. I have not found a straight forward answer. Any help/pointing in the appropriate direction is appreciated.
Related links:
Do stored procedures lock tables/rows?
SQL Server - How to lock a table until a stored procedure finishes
Microsoft docs say xp_cmdshell will run synchronously. Triggers run synchronously too. So, if your exe gets stuck, it will hang the trigger, which will hang the insert, and other stuff. msdn.microsoft.com/en-us/library/ms175046.aspx#remarks

SQL Server stored procedure syntax explanation

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE <Procedure_Name, sysname, ProcedureName>
-- Add the parameters for the stored procedure here
<#Param1, sysname, #p1> <Datatype_For_Param1, , int> = <Default_Value_For_Param1, , 0>,
<#Param2, sysname, #p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT <#Param1, sysname, #p1>, <#Param2, sysname, #p2>
END
GO
Can anyone help me understand the syntax of SQL? I am new to this forum as well as in SQL Server. I would like to know about the GO command in stored procedure at the end of script. Thanks in advance.
See the MSDN:
Signals the end of a batch of Transact-SQL statements to the SQL
Server utilities.
Also note that GO is not a TSQL statement.
SQL Server utilities interpret GO as a signal that they should send
the current batch of Transact-SQL statements to an instance of SQL
Server. The current batch of statements is composed of all statements
entered since the last GO, or since the start of the ad hoc session or
script if this is the first GO.
Its a batch seperator used in SQL Server Management Studio. You can go to Tools--> Options--> Query Execution

Switching from one database to another within the same script

I would like to know how I can switch from one database to another within the same script. I have a script that reads the header information from a SQL Server .BAK file and loads the information into a test database. Once the information is in the temp table (Test database) I run the following script to get the database name.
This part works fine.
INSERT INTO #HeaderInfo EXEC('RESTORE HEADERONLY
FROM DISK = N''I:\TEST\database.bak''
WITH NOUNLOAD')
DECLARE #databasename varchar(128);
SET #databasename = (SELECT DatabaseName FROM #HeaderInfo);
The problem is when I try to run the following script nothing happens. The new database is never selected and the script is still on the test database.
EXEC ('USE '+ #databasename)
The goal is switch to the new database (USE NewDatabase) so that the other part of my script (DBCC CHECKDB) can run. This script checks the integrity of the database and saves the results to a temp table.
What am I doing wrong?
You can't expect a use statement to work in this fashion using dynamic SQL. Dynamic SQL is run in its own context, so as soon as it has executed, you're back to your original context. This means that you'd have to include your SQL statements in the same dynamic SQL execution, such as:
declare #db sysname = 'tempdb';
exec ('use ' + #db + '; dbcc checkdb;')
You can alternatively use fully qualified names for your DB objects and specify the database name in your dbcc command, even with a variable, as in:
declare #db sysname = 'tempdb';
dbcc checkdb (#db);
You can't do this because Exec scope is limited to dynamic query. When exec ends context is returned to original state. But context changes in Exec itself. So you should do your thing in one big dynamic statement like:
DECLARE #str NVARCHAR(MAX)
SET #str = 'select * from table1
USE DatabaseName
select * from table2'
EXEC (#str)

Resources