Why I cannot change database dynamically SQL Server 2008 - sql

The following is not working and I am definitely missing the obvious but would be nice if somebody could explain why is not working. I need to change db dynamically.
The print out looks good but does not change db in the SQL Server drop down.
DECLARE #tempSql nvarchar(4000);
DECLARE #FinalSQL nvarchar(4000);
DECLARE #dbName varchar(100);
SET #dbName = 'Pubs';
SET #tempSql = 'SELECT DB_NAME()';
SET #FinalSQL = 'USE ' + #dbName + '; EXEC sp_executesql N''' + #tempSql + '''';
EXEC (#FinalSQL)

If SQLCMD mode is an option for your (within SSMS, for example), you can do this:
:setvar dbname Pubs
USE [$(dbname)]
SELECT DB_NAME()
Or, your original syntax was pretty close. Try this:
DECLARE #db AS NVARCHAR(258);
SET #db = QUOTENAME(N'Pubs');
EXEC(N'USE ' + #db + N'; EXEC(''SELECT DB_NAME();'');');
GO

There is a way to access data from a specific database by using this syntax :
FROM DatabaseName..TableName
maybe you should use a dynamic database name in your scripts, then change it whenever you need
otherwise, take a look at this : http://www.sqlteam.com/article/selecting-data-from-different-databases

Executing the dynamic SQL is done in a scope of its own.
So you do change the current database, as you see, but only within the scope of the dynamic SQL.

Related

how to declare a database

How can I declare a database as when I try the following:
declare #Database1 [$(Database1)]
It states 'Column, parameter, or variable #8: Cannot find data type Database1'.
UPDATE:
So what I am trying to do is create a variable so that it refers to a database, and then I can include that variable in an OUTPUT statement. I'm using visual studio where by I'm using a reference to call the database, but want that reference to be set as a variable:
At the moment I have:
declare #Database1 [$(Database1)]
...
OUTPUT ''+#Database1+'.dbo.Package' 'TableName', 'PackageID', inserted.PackageId,
Core.updXMLFragment ('StatusID', inserted.StatusID, Deleted.StatusID)
INTO #OutputList
Like I said I get an error when I try the above. I know [$(Database1)] is a legit reference to a database, just need to know how to include this into an OUTPUT statement so that when I move it SSMS, it displays it as [Database1] and not an error.
The reason for [$(Database1)] is so that if we ever change the database name, we don't have to change it in our code as we are using this reference to call on the database rather than the database name itself.
One can use sqlcmd Tool to declare the same.
:setvar dbname "TEST"
CREATE DATABASE $(dbname)
GO
ALTER DATABASE $(dbname) SET COMPATIBILITY_LEVEL = 90
GO
ALTER DATABASE $(dbname) SET RECOVERY SIMPLE
GO
try to use Dynamic SQL
DECLARE #Database SYSNAME = 'master'
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = '
USE ' + QUOTENAME(#Database) + '
SELECT DB_NAME()
'
--PRINT #SQL
EXEC sys.sp_executesql #SQL

Executing a "sp_executesql #sqlcommand" Syntax Error

I am setting up a SQL script that creates a database from a variable name, and then takes that newly created database and restores it from a .bak file. I am having issues with some syntax in one of my commands that I am setting up, and wanted to ask if anybody could help me spot my syntax error? I am only going to paste my troubled snippet of code and its declarations, and if I am correct the issue lies in the way that I am declaring the file name paths. I have tried setting the paths to variables, but I still received errors due to the apostrophe placement. Thanks!!!
declare #DBname varchar(10), #sqlcommand Nvarchar(max)
set #DBname = 'testdb'
Code to create database, and set new database to single user mode
--restore database
set #sqlcommand = N'Restore DATAbase ' + #DBname + ' from disk = ''C:/loc_smartz_db0_template.bak'' with move '
+ #DBname + ' to ''C:/ProgramFiles/Microsoft SQL Server/MSSQL/Data/TestDatabase1.mdf'', move ' + #DBname + ' to ''C:/ProgramFiles/Microsoft SQL Server/MSSQL/Data/TestDatabase1.ldf'', Replace'
EXECUTE sp_executesql #sqlcommand
Code that sets database back to multiuser, and prints that the database was successfully created
It looks like the previous posters have fixed your problem, but this may have been avoided if you had used dynamic sql in the 'best practice' manner. Concatenating the string together as a mixture of variables and string literals is not ideal as it makes working with apostrophes difficult (as shown here).
A better way is to write your sql as
declare #DBname nvarchar(255) = 'testdb'
,#BakName nvarchar(255) = 'C:\loc_smartz_db0_template.bak'
,#MovemdfName nvarchar(255) = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data\TestDatabase1.mdf'
,#MoveldfName nvarchar(255) = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data\TestDatabase1.ldf'
,#sqlcommand nvarchar(max)
,#paramList nvarchar(max)
set #paramList = '#DBname nvarchar(255), #BakName nvarchar(255), #MovemdfName nvarchar(255), #MoveldfName nvarchar(255)'
set #sqlcommand = N'Restore DATAbase #DBname from disk = #BakName with move #DBname to #MovemdfName, move #DBname to #MoveldfName, Replace'
exec sp_executesql #statement = #sqlcommand
,#params = #paramList
,#DBname = #DBname
,#BakName = #BakName
,#MovemdfName = #MovemdfName
,#MoveldfName = #MoveldfName
This way, your sql command is very easy to read and maintain. Note that you don't have to mess around with escaping the apostrophes in the variable values either if you have spaces in your pathnames.
It also has the advantage (if you have the code in a stored proc) of allowing SQL Server to reuse execution plans which will improve performance.
See here for more information.
Two things.
First, you have to put single quotes around the database file logical name, e.g.
from
...with move testdb to 'C:/ProgramFiles/Microsoft SQL Server/MSSQL/Data/TestDatabase1.mdf'
to
...with move 'testdb' to 'C:/ProgramFiles/Microsoft SQL Server/MSSQL/Data/TestDatabase1.mdf'
making it
set #sqlcommand = N'Restore DATAbase ' + #DBname + ' from disk = ''C:\loc_smartz_db0_template.bak'' with move '''
+ #DBname + ''' to ''C:\ProgramFiles\Microsoft SQL Server\MSSQL\Data\TestDatabase1.mdf'', move '''
+ #DBname + ''' to ''C:\ProgramFiles\Microsoft SQL Server\MSSQL\Data\TestDatabase1.ldf'', Replace'
Second, use backslashes \, not slashes. (Maybe this works, but it didn't in my quick tests.)

Script to create a schema using a variable

I'm surprised that this hasn't come up before but I haven't found anything in searches either here or elsewhere. I found something vaguely similar which indicates that the problem is that the Use command in my script lasts only for that one line, but there was no indication of how to work around that.
What I'm trying to do: Create a generic script to create a "template" database with all of my common schemas and tables. All of the variables (such as the database name) are intended to be set in the header so that they can be changed as needed and the script can be just run without needing to do any risky search and replace operations to change hard coded values.
What the problem is: I can't get the schemas to generate in the right database; they're all generating in Master. Trying to explicitly set the database didn't help; I just received runtime errors.
My skill level: Long time Access user but still in the foothills of exploring SQL Server. I'm sure (well, hoping) this this will be ridiculously easy for someone further up the slope.
Does anyone know how I can do something like this? (Existing code shown below.)
DECLARE #DBName NVARCHAR(50) = 'TheDBName';
-- Assume that there's a bunch of code to drop and create the database goes here.
-- This code executes correctly.
SET #SQL = 'Use [' + #DBName + ']';
Print #SQL;
EXEC(#SQL);
SET #Counter = 1;
WHILE #Counter <=3
BEGIN
SET #SQL = 'CREATE SCHEMA [' +
CASE #Counter
WHEN 1 THEN 'Schema1'
WHEN 2 THEN 'Schema2'
WHEN 3 THEN 'Schema3'
END
SET #SQL = #SQL + '] AUTHORIZATION [dbo]';
PRINT 'Creating Schemas, ' + #SQL;
Exec(#SQL);
SET #Counter = #Counter + 1;
END
The use command only changes the current db in the scope you are in and dynamic SQL runs in a scope of its own.
Try this from master
declare #SQL nvarchar(max)
set #SQL = N'use tempdb; print db_name()'
exec(#SQL)
print db_name()
Result:
tempdb
master
Try this:
DECLARE #DBName NVARCHAR(50) = 'TheDBName';
DECLARE #SQL NVARCHAR(max)
DECLARE #SQLMain NVARCHAR(max)
DECLARE #Counter int
SET #SQLMain = 'Use [' + #DBName + ']; exec(#SQL)';
SET #Counter = 1;
WHILE #Counter <=3
BEGIN
SET #SQL = 'CREATE SCHEMA [' +
CASE #Counter
WHEN 1 THEN 'Schema1'
WHEN 2 THEN 'Schema2'
WHEN 3 THEN 'Schema3'
END
SET #SQL = #SQL + '] AUTHORIZATION [dbo]';
EXEC sp_executesql #SQLMain, N'#SQL nvarchar(max)', #SQL;
SET #Counter = #Counter + 1;
END
If you run USE statement inside EXEC() then run other statements also in EXEC()
but
you have to use USE databasename stmt. in every EXEC()
Other answers have explained your immediate problem but since this is really a deployment issue, as a general solution you might want to try using SQLCMD variables that you can then set at runtime from the command line. This would allow you to pass the database name and/or schema names into scripts dynamically so you can then automate your deployment using batch files or VS database projects.

How to use a varying database?

I want to use a database which name is stored in a variable. How do I do this?
I first thought this would work but it doesn't:
exec('use '+#db)
That will not change database context
Suggestions anyone?
Unfortunately I don't know of a direct solution to this one. The nearest working version is:
DECLARE #db nvarchar(MAX)
SET #db = 'use DBname'
Exec sp_executesql #db
but this only changes the context for the length of the procedure call. However, more statements can be included in that call to make use of the context:
DECLARE #sql nvarchar(MAX)
SET #sql = 'use DBName SELECT * FROM Table1'
Exec sp_executesql #sql
If you absolutely have to do this using dynamic SQl, I prefer this:
DECLARE #sql nvarchar(MAX)
declare #databasename varchar (20)
Set #databasename = mydatabase
SET #sql = 'SELECT * FROM ' + #databasename + 'dbo.Table1'
Exec sp_executesql #sql
The reason I prefer it is that you can extend it to use multipe datbases in the same query if need be.
I havea a concern that you don't know the datbase name for each table already without resorting to dynamic means. In other words, why can't you write:
SELECT * FROM mydatabase.dbo.Table1
If you have multiple databases with the same table names, likely you have a design problem.
The use statement is only in scope inside the exec block. Therefore you would have to do everything else in the same exec:
exec('use '+ #db + '
--do other stuff'
)
Presumably you know all the possible database names. One (slightly inelligant) way of doing this would be to use a CASE or multiple IF statements to test the variable and hardcode the USE statement for each case.

Fully qualified table names with SP_ExecuteSql to access remote server

Trying to update a table on a linked server (SQL 2000/2005) but my server name will not be known ahead of time. I'm trying this:
DECLARE #Sql NVARCHAR(4000)
DECLARE #ParamDef NVARCHAR(4000)
DECLARE #SERVER_NAME VARCHAR(35)
SET #Sql = 'UPDATE
#server_name_param.dba_sandbox.dbo.SomeTable
SET SomeCol=''data'''
SET #ParamDef = N'#server_name_param VARCHAR(35)'
print #Sql
exec sp_executesql #Sql, #ParamDef, #server_name_param=#SERVER_NAME
Which returns this:
UPDATE
#server_name_param.dba_sandbox.dbo.SomeTable
SET SomeCol='data'
Msg 170, Level 15, State 1, Line 2
Line 2: Incorrect syntax near '.'.
Any ideas? Is there anyway I view the SQL statement that is being executed after the parameters are bound?
You'll have to do this, it can't be parameterised
....
SET #Sql = 'UPDATE ' + #server_name_param + '.dba_sandbox.dbo.SomeTable SET SomeCol=''data'''
....
Edit: There is another way which I used back in my pure DBA days
EXEC sp_setnetname 'AdhocServer', #SERVER_NAME
UPDATE AdhocServer.dba_sandbox.dbo.SomeTable SET SomeCol 'data'
EXEC sp_setnetname 'AdhocServer', 'MeaninglessValue'
sp_setnetname is there from SQL Server 2000 to 2008
Edit2. Permissions:
Try EXECUTE AS LOGIN = 'login_name' , where login_name is a superuser
I've not really used this (I use "AS USER" for testing), so not sure of the finer points...
Edit 3: for concurrency, consider using sp_getapplock and a stored procedure, or some other concurrency control mechanism.
You cannot do this with parameters directly - you would have to use dynamic SQL, or send the server name as a parameter to an SP that does dynamic SQL:
DECLARE #template NVARCHAR(4000)
DECLARE #Sql NVARCHAR(4000)
DECLARE #SERVER_NAME VARCHAR(35)
SET #template = 'UPDATE {#server_name_param}.dba_sandbox.dbo.SomeTable SET SomeCol=''data'''
SET #sql = REPLACE(#template, '{#server_name_param}', #SERVER_NAME)
print #Sql
exec sp_executesql #Sql -- OR EXEC ( #sql )
I like gbn's trick. I didn't know that one and I'm gonna have to research that some more.
Since I didn't know that trick, I've had to use dynamic sql in similar situations in the past (like what Cade posted). When that happens I would normally query an information schema view to make sure the parameter value is a real database object before building the query. That way I'm sure it's not an injection attempt.