How to complete this query to remove replication and then drop database? - sql-server

I'm trying to remove replication and drop database all in one query when selecting the database from right-click context menu and running custom script.
I'm able to remove replication, but I get the error below when trying to drop database.
DECLARE #DB varchar(50)
SELECT #DB = DB_NAME()
USE master
EXEC sp_removedbreplication #DB
GO
DROP DATABASE #DB

You cannot use a variable to specify the database to drop.
You need to use dynamic SQL for this, e.g.:
DECLARE #sql nvarchar(max);
SET #sql = 'DROP DATABASE ' + QUOTENAME(#DB);
EXECUTE sp_executesql #sql;

Related

Could not find stored procedure 'sp_msforeachtable' while looping through server for stats (SSMS)

I've written this to loop through each database on a server, collecting the statistics for each table and storing them in a temp table. Eventually, I'll integrate this into a more permanent structure, but for now I'm just trying to get this working. My problem is, after 57 databases, I get the error stating it can't find the stored procedure sp_msforeachtable.
I've verified that this stored procedure exists on every database on the server and on the server level.
I've excluded this database in the findings by adding it to the "where name not in" condition, and it just moves to the next one in the list and gives the same error.(I've confirmed it exists on the next database also). I've actually done this for the next 6 databases.
This is causing me to not collect accurate information. Am I running out of resources somewhere?
DECLARE #Database TABLE (DbName SYSNAME);
IF OBJECT_ID('tempdb.dbo.#TableLvlSizes', 'U') IS NOT NULL
BEGIN
PRINT 'dropping table'
DROP TABLE tempdb.dbo.#TableLvlSizes;
END
CREATE TABLE #TableLvlSizes (
TableName nvarchar(128)
,NumberOfRows varchar(50)
,ReservedSpace varchar(50)
,TableDataSpace varchar(50)
,IndexSize varchar(50)
,unused varchar(50))
DECLARE #DbName AS SYSNAME;
DECLARE #Sql1 AS VARCHAR(MAX);
SET #DbName = '';
INSERT INTO #Database (DbName)
SELECT NAME
FROM sys.databases
where name not in ('tempdb')
ORDER BY NAME ASC;
WHILE #DbName IS NOT NULL
BEGIN
SET #DbName = (
SELECT MIN(DbName)
FROM #Database
WHERE DbName > #DbName
);
print #DbName;
SET #Sql1 =
'USE ' + #DbName + '; ' + '
Exec sp_msforeachtable
''insert into #TableLvlSizes exec sp_spaceused [?]''
'
Exec (#SQL1);
END
If someone is using Azure SQL, they will not find sp_MSforeachtable since it is not available in Azure SQL.
You may need to create one for yourself.
Since you already verified that the stored procedure does in fact exist, I believe your database is case sensitive. Therefore, the error is still accurate. Basically, the stored procedure with the case you used does not exist. The actual procedure name is sp_MSforeachtable
In your code, you are using the following:
Exec sp_msforeachtable
If you change your code to use the proper case for the stored procedure to be sp_MSforeachtable, it should work:
SET #Sql1 =
'USE ' + #DbName + '; ' + '
Exec sp_MSforeachtable
''insert into #TableLvlSizes exec sp_spaceused [?]'''

MS SQL loop through all DBs. Script works to alter table but not stored procedure

I use a piece of code to loop through all the databases on an MS SQL server. It works fine for altering a column on a table and also for updating the data. But I continue to get errors when trying to alter a stored procedure. Here is the code:
use master
declare #dbname varchar(100)
,#sql varchar(max)
declare db_cur cursor for
SELECT name
FROM sys.databases where ([name] like 'ce%')
and [state] = 0
open db_cur
fetch next from db_cur into #dbname
while ##FETCH_STATUS = 0
begin
set #sql=
'ALTER TABLE ['+#dbname+'].[dbo].MyStuff
ADD myNewColumn bit NULL DEFAULT(0)
'
exec(#sql)
fetch next from db_cur into #dbname
end
close db_cur
deallocate db_cur
So the code above works perfectly fine. But when I alter that code to instead do an alter stored procedure I receive the message below:
'CREATE/ALTER PROCEDURE' does not allow specifying the database name as a prefix to the object name.
I realized that the message stated I can't use the database name in the front of the procedure like I was doing here: ALTER procedure ['+#dbname+'].[dbo].[spSelectSomething]. But I haven't been able to figure out a way around the issue. Thanks for your help.
You need to nest dynamic SQL for this task because a proc CREATE or ALTER must be the first statement in the batch:
SET #sql= N'EXEC(N''USE ' + QUOTENAME(#dbname) + N';EXEC(N''''CREATE PROC...;'''')'')';

Stored procedure to alter database recovery mode isnt compiling

I am trying to create a stored procedure that would be generic. I am trying to alter a database and set the recovery mode to either simple or full. It would accept database name and mode as parameter.
The SQL query executes in the context of the master database and alters the database specified. I am trying to incorporate it via Execute SQL task in SSIS. I need the stored procedure to reside in the database that is going to perform the operation on. Not sure how that is going to work. USE database keyword is not allowed in the stored procedure...
The original query works fine but I am facing an issue while trying to execute the stored procedure in the database.It says 'RECOVERY' is not a recognized SET option.
Original query:
use master
ALTER DATABASE XYZ
SET RECOVERY FULL
Stored procedure:
USE XYZ
GO
CREATE PROCEDURE DatabaseRecoveryMode
(#mode varchar(10),
#database varchar(50))
AS
BEGIN
ALTER DATABASE #database
SET RECOVERY #mode
END
The ALTER DATABASE documentation shows the recovery model is a keyword, not a variable. You'll need to construct and execute a dynamic SQL statement for this.
CREATE PROCEDURE dbo.DatabaseRecoveryMode
(
#mode nvarchar(11),
#database sysname
)
AS
IF #mode NOT IN(N'SIMPLE', N'BULK_LOGGED', N'FULL')
BEGIN
RAISERROR('Recovery model must be SIMPLE, BULK_LOGGED, OR FULL', 16, 1);
RETURN 1;
END;
DECLARE #SQL nvarchar(MAX) = N'ALTER DATABASE '
+ QUOTENAME(#database)
+ N' SET RECOVERY '+ #mode + N';';
EXECUTE(#SQL);
GO
You need to use dynamic SQL
USE XYZ
GO
Create Procedure DatabaseRecoveryMode
(
#mode varchar(10),
#database varchar(50)
)
AS
begin
DECLARE #SQL NVARCHAR(MAX)
DECLARE #db NVARCHAR(60), #Use NVARCHAR(100)
SET #db = N'master'
SET #Use = N'Use ' + #db
SET #SQL = #Use + N' ALTER DATABASE '+ #database + N' SET RECOVERY ' + #mode ;
--SELECT #SQL
EXEC sys.sp_executesql #SQL ;
end
GO

Create A View With Dynamic Sql

I'm trying to create a dynamic database creation script.
There are a lot of steps and we create this database often so the script looks something like this.
DECLARE #databaseName nvarchar(100) = 'DatabaseName'
EXEC('/*A lot of database creation code built off of #databaseName*/')
This is all well and good except for one view that we'd like to create in #databaseName.
I've tried four different ways to create this view without success:
My first thought was to simply set the database context and then create the view in one script. Unfortunately, this didn't work because CREATE VIEW must be the first statement in its query block (details).
--Result: Error message, "'CREATE VIEW' must be the first statement in a query batch"
EXEC
('
USE [' + #databaseName + ']
CREATE VIEW
')
To get around (1) I tried to set the context separately so that CREATE VIEW would be the first command in the EXEC. This did create the view but did so within my current context and not #databaseName. It seem that the effects of calling USE in EXEC only persist until the end of that EXEC statement (details).
--Result: The view is created in the currently active database rather than #databaseName
EXEC ('USE [' + #databaseName + ']')
EXEC ('CREATE VIEW')
Next I tried putting everything back into one script but included a GO command in order to make CREATE VIEW the first command in a new query block. This failed because GO isn't allowed within an EXEC script (details).
--Result: Error message, "Incorrect syntax near 'GO'"
EXEC
('
USE [' + #databaseName + ']
GO
CREATE VIEW
')
Finally I tried to specify the target database as part of the CREATE VIEW command. In this case the script failed because CREATE VIEW doesn't allow the database to be specified as part of its creation (details).
--Result: Error message, "'CREATE/ALTER VIEW' does not allow specifying the database name as a prefix to the object name"
EXEC ('CREATE VIEW [' + #databaseName + '].[dbo].[ViewName]')
Any suggestions? I think this should be a common use case but Google wasn't able to help me.
You can do this by double nesting the dynamic SQL statements then:
begin tran
declare #sql nvarchar(max) =
N'use [AdventureWorks2012];
exec (''create view Test as select * from sys.databases'')';
exec (#sql);
select * from AdventureWorks2012.sys.views
where name = 'Test'
rollback tran
Instead of double nesting, another approach is to create a stored procedure whose only purpose is to executes dynamic SQL
CREATE PROCEDURE [dbo].[util_CreateViewWithDynamicSQL]
#sql nvarchar(max)
AS
BEGIN
SET NOCOUNT ON;
EXECUTE (#sql)
END
The stored procedure above can be re-used. Anytime you need to create a view just call the stored procedure and pass it the dynamic sql.
EXECUTE util_CreateViewWithDynamicSQL 'create view Test as select * from sys.databases'
I prefer this approach because dynamic sql is confusing enough and adding double nesting complicates it further.

How to use a variable for the database name in T-SQL?

I use the database name in several places in my script, and I want to be able to quickly change it, so I'm looking for something like this:
DECLARE #DBNAME VARCHAR(50)
SET #DBNAME = 'TEST'
CREATE DATABASE #DBNAME
GO
ALTER DATABASE #DBNAME SET COMPATIBILITY_LEVEL = 90
GO
ALTER DATABASE #DBNAME SET RECOVERY SIMPLE
GO
But it doesn't work. So what's the correct way to write this code?
Put the entire script into a template string, with {SERVERNAME} placeholders. Then edit the string using:
SET #SQL_SCRIPT = REPLACE(#TEMPLATE, '{SERVERNAME}', #DBNAME)
and then run it with
EXECUTE (#SQL_SCRIPT)
It's hard to believe that, in the course of three years, nobody noticed that my code doesn't work!
You can't EXEC multiple batches. GO is a batch separator, not a T-SQL statement. It's necessary to build three separate strings, and then to EXEC each one after substitution.
I suppose one could do something "clever" by breaking the single template string into multiple rows by splitting on GO; I've done that in ADO.NET code.
And where did I get the word "SERVERNAME" from?
Here's some code that I just tested (and which works):
DECLARE #DBNAME VARCHAR(255)
SET #DBNAME = 'TestDB'
DECLARE #CREATE_TEMPLATE VARCHAR(MAX)
DECLARE #COMPAT_TEMPLATE VARCHAR(MAX)
DECLARE #RECOVERY_TEMPLATE VARCHAR(MAX)
SET #CREATE_TEMPLATE = 'CREATE DATABASE {DBNAME}'
SET #COMPAT_TEMPLATE='ALTER DATABASE {DBNAME} SET COMPATIBILITY_LEVEL = 90'
SET #RECOVERY_TEMPLATE='ALTER DATABASE {DBNAME} SET RECOVERY SIMPLE'
DECLARE #SQL_SCRIPT VARCHAR(MAX)
SET #SQL_SCRIPT = REPLACE(#CREATE_TEMPLATE, '{DBNAME}', #DBNAME)
EXECUTE (#SQL_SCRIPT)
SET #SQL_SCRIPT = REPLACE(#COMPAT_TEMPLATE, '{DBNAME}', #DBNAME)
EXECUTE (#SQL_SCRIPT)
SET #SQL_SCRIPT = REPLACE(#RECOVERY_TEMPLATE, '{DBNAME}', #DBNAME)
EXECUTE (#SQL_SCRIPT)
You can also use sqlcmd mode for this (enable this on the "Query" menu in Management Studio).
:setvar dbname "TEST"
CREATE DATABASE $(dbname)
GO
ALTER DATABASE $(dbname) SET COMPATIBILITY_LEVEL = 90
GO
ALTER DATABASE $(dbname) SET RECOVERY SIMPLE
GO
EDIT:
Check this MSDN article to set parameters via the SQLCMD tool.
Unfortunately you can't declare database names with a variable in that format.
For what you're trying to accomplish, you're going to need to wrap your statements within an EXEC() statement. So you'd have something like:
DECLARE #Sql varchar(max) ='CREATE DATABASE ' + #DBNAME
Then call
EXECUTE(#Sql) or sp_executesql(#Sql)
to execute the sql string.
You cannot use a variable in a create table statement. The best thing I can suggest is to write the entire query as a string and exec that.
Try something like this:
declare #query varchar(max);
set #query = 'create database TEST...';
exec (#query);

Resources