How to use a varying database? - sql

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.

Related

Is it possible to set a part of a select statement in a variable

I have a query of which the select-part is really long. I'd like to split this in several pieces, especially because some parts are in there twice or even more often.
What I'd like is something like the following:
Declare #SQLPart as varchar(1000)
Set #SQLPart = 'Field1,
case ... as Field2,'
Select ..., #SQLPart, ... From .....
Unfortunately this results error messages. I tried something like EXEC(#SQLPart) as well but of course this also didn't work. How would I solve this?
Yes, dynamic sql and sp_executesql:
CREATE TABLE ##Temp (Field1 int, Field2 int)
Declare #SQLPart nvarchar(1000)
Set #SQLPart = N'Field1, Field2 '
DECLARE #SQL nvarchar(1000) = N'SELECT ' + #SQLPart + 'FROM ##Temp'
PRINT #SQL
EXEC sp_executesql #SQL
DROP TABLE ##Temp
Your SQL code must be nvarchar type.
Alse sp_executesql is better than EXECUTE function, when you have many similar queries, sp_executesql caches executaion plans, and it can be better in perfomance.
You can use dynamic sql here,and use a EXECUTE keyword to execute this dynamic query
Declare #SQLPart as varchar(1000)
Set #SQLPart = 'Field1,
case ... as Field2,'
EXECUTE ('SELECT ....,'+#SQLPart+',... FROM ...')
SQL Server does not support Macro-Substitution, so you would have to use Dynamic SQL.
Declare #SQL varchar(max) ='Select ... ' + #SQLPart + '... from ...'
Exec(#SQL)

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.

Why I cannot change database dynamically SQL Server 2008

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.

statement "USE #dbname" doesn't work, why? How to do that?

I've got this t-sql snippet:
DECLARE #db_name varchar(255);
SET #db_name = 'MY_DATABASE'; -- assuming there is database called 'my_database'
USE #db_name -- this line ends with error "Incorrect syntax near '#db'."
But USE with variable (third line of snippet) doesn't work.
Why it doesn't work?
You cannot provide the name of the database for USE statement in a variable.
As you have noticed, the USE statement does not accept a variable as parameter. The only alternative that quickly comes to mind is quite crude and extremely error prone, but here you go:
EXEC ('USE ' + #db_name + '
SELECT * FROM some_table
INSERT INTO some_table VALUES (1)')
I hope that someone else can do better :-)
SQL Server will not accept the USE statement with a variable.
To use database names dynamically, you have to create dynamic SQL statements with (almost) fully qualified names as follows:
Declare #SQL VarChar (100)
SET #SQL = 'SELECT * FROM ' + #DatabaseName + '.dbo.TableName'
and then you execute it using sp_SQLExec
The way I do this is with an if statement:
if #DBName = 'DB1'
<query with DB1>
else
<query with DB2>

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.