sql use statement with variable - sql-server

I'm trying to switch the current database with a SQL statement.
I have tried the following, but all attempts failed:
USE #DatabaseName
EXEC sp_sqlexec #Sql -- where #Sql = 'USE [' + #DatabaseName + ']'
To add a little more detail.
EDIT: I would like to perform several things on two separate database, where both are configured with a variable. Something like this:
USE Database1
SELECT * FROM Table1
USE Database2
SELECT * FROM Table2

The problem with the former is that what you're doing is USE 'myDB' rather than USE myDB.
you're passing a string; but USE is looking for an explicit reference.
The latter example works for me.
declare #sql varchar(20)
select #sql = 'USE myDb'
EXEC sp_sqlexec #Sql
-- also works
select #sql = 'USE [myDb]'
EXEC sp_sqlexec #Sql

exec sp_execsql #Sql
The DB change only lasts for the time to complete #sql
http://blog.sqlauthority.com/2007/07/02/sql-server-2005-comparison-sp_executesql-vs-executeexec/

I have the same problem, I overcame it with an ugly -- but useful -- set of GOTOs.
The reason I call the "script runner" before everything is that I want to hide the complexity and ugly approach from any developer that just wants to work with the actual script. At the same time, I can make sure that the script is run in the two (extensible to three and more) databases in the exact same way.
GOTO ScriptRunner
ScriptExecutes:
--------------------ACTUAL SCRIPT--------------------
-------- Will be executed in DB1 and in DB2 ---------
--TODO: Your script right here
------------------ACTUAL SCRIPT ENDS-----------------
GOTO ScriptReturns
ScriptRunner:
USE DB1
GOTO ScriptExecutes
ScriptReturns:
IF (db_name() = 'DB1')
BEGIN
USE DB2
GOTO ScriptExecutes
END
With this approach you get to keep your variables and SQL Server does not freak out if you happen to go over a DECLARE statement twice.

Just wanted to thank KM for his valuable solution.
I implemented it myself to reduce the amount of lines in a shrinkdatabase request on SQLServer.
Here is my SQL request if it can help anyone :
-- Declare the variable to be used
DECLARE #Query varchar (1000)
DECLARE #MyDBN varchar(11);
-- Initializing the #MyDBN variable (possible values : db1, db2, db3, ...)
SET #MyDBN = 'db1';
-- Creating the request to execute
SET #Query='use '+ #MyDBN +'; ALTER DATABASE '+ #MyDBN +' SET RECOVERY SIMPLE WITH NO_WAIT; DBCC SHRINKDATABASE ('+ #MyDBN +', 1, TRUNCATEONLY); ALTER DATABASE '+ #MyDBN +' SET RECOVERY FULL WITH NO_WAIT'
--
EXEC (#Query)

try this:
DECLARE #Query varchar(1000)
DECLARE #DatabaseName varchar(500)
SET #DatabaseName='xyz'
SET #Query='SELECT * FROM Server.'+#DatabaseName+'.Owner.Table1'
EXEC (#Query)
SET #DatabaseName='abc'
SET #Query='SELECT * FROM Server.'+#DatabaseName+'.Owner.Table2'
EXEC (#Query)

I case that someone need a solution for this, this is one:
if you use a dynamic USE statement all your query need to be dynamic, because it need to be everything in the same context.
You can try with SYNONYM, is basically an ALIAS to a specific Table, this SYNONYM is inserted into the sys.synonyms table so you have access to it from any context
Look this static statement:
CREATE SYNONYM MASTER_SCHEMACOLUMNS FOR Master.INFORMATION_SCHEMA.COLUMNS
SELECT * FROM MASTER_SCHEMACOLUMNS
Now dynamic:
DECLARE #SQL VARCHAR(200)
DECLARE #CATALOG VARCHAR(200) = 'Master'
IF EXISTS(SELECT * FROM sys.synonyms s WHERE s.name = 'CURRENT_SCHEMACOLUMNS')
BEGIN
DROP SYNONYM CURRENT_SCHEMACOLUMNS
END
SELECT #SQL = 'CREATE SYNONYM CURRENT_SCHEMACOLUMNS FOR '+ #CATALOG +'.INFORMATION_SCHEMA.COLUMNS';
EXEC sp_sqlexec #SQL
--Your not dynamic Code
SELECT * FROM CURRENT_SCHEMACOLUMNS
Now just change the value of #CATALOG and you will be able to list the same table but from different catalog.

If SQLCMD is an option, it supports scripting variables above and beyond what straight T-SQL can do. For example: http://msdn.microsoft.com/en-us/library/ms188714.aspx

You can do this:
Declare #dbName nvarchar(max);
SET #dbName = 'TESTDB';
Declare #SQL nvarchar(max);
select #SQL = 'USE ' + #dbName +'; {can put command(s) here}';
EXEC (#SQL);
{but not here!}
This means you can do a recursive select like the following:
Declare #dbName nvarchar(max);
SET #dbName = 'TESTDB';
Declare #SQL nvarchar(max);
SELECT #SQL = 'USE ' + #dbName + '; ' +(Select ... {query here}
For XML Path(''),Type)
.value('text()[1]','nvarchar(max)');
Exec (#SQL)

Use exec sp_execsql #Sql
Example
DECLARE #sql as nvarchar(100)
DECLARE #paraDOB datetime
SET #paraDOB = '1/1/1981'
SET #sql=N'SELECT * FROM EmpMast WHERE DOB >= #paraDOB'
exec sp_executesql #sql,N'#paraDOB datetime',#paraDOB

-- If you are using a variable for the database name.
-- Try something like this.
DECLARE #DBName varchar(50)
Set #DBName = 'Database1'; /* could be passed in by a parameter. */
IF( #DBName = 'Database1')
Begin
USE [Database1];
SELECT FROM Table1;
End
IF( #DBName = 'Database2')
Begin
USE [Database2];
SELECT FROM Table2;
End
IF( #DBName is null)
Begin
USE [Database1];
End

Related

Running dynamic queries on linked server

I wonder, is it possible to make this work?
declare #SQL varchar(max),
#query varchar(max) ='select * from Table'
select #SQL='USE LinkedServer.DBName ' + #query'
exec (#SQL)
It works just fine if you use 'USE' to run queries on the same server, but not on Linked ones.
What I want is to create procedure which will run some dynamic queries on different servers and dbs, and I want to pass ServerName and DBName as a parameter to that sp.
NOTE: I don't want to use it this way:
declare #SQL varchar(max)
select #SQL='
select * from LinkedServer.DBName..Table'
exec (#SQL)
Since that select statement will be retrieved from table
Sure, little-known trick to execute the remote database's sp_executesql:
DECLARE #LinkedServer sysname = N'LinkedServer',
#DatabaseName sysname = N'DBName';
-- above are input params
DECLARE #exec nvarchar(4000), #sql nvarchar(max);
SET #sql = N'SELECT * FROM dbo.Table;';
-- or SELECT #sql = DynamicSQL FROM dbo.SomeLocalTable WHERE...
SET #exec = QUOTENAME(#LinkedServer) + N'.'
+ QUOTENAME(#DatabaseName)
+ N'.sys.sp_executesql';
EXEC #exec #sql;
Don't use parentheses. EXEC('string') is evil, evil, evil.

how to parametrize a database in a distributed query

I need to define the database as a variable in a query like this:
insert into Database1.dbo.Table1
select * from Database2.dbo.Table1
In my case all the databases have the same schema.
I can do with dynamic sql of course, but is it a way to have a syntax like:
insert into Database1.dbo.Table1
select * from #ChosenDatabase.dbo.Table1
?
Thanks
What you are trying to achieve is not possible; You have to use Dynamic SQL :
DECLARE #SQLQuery varchar(300)
DECLARE #TableName varchar(100)
SET #TableName = ChosenDatabase.dbo.Table1
SET #SQLQuery = 'INSERT INTO Database1.dbo.Table1 SELECT * FROM ' + #TableName
EXEC(#SQLQuery);
Declare #dbname varchar(200)
Declare #sql varchar(1000)
now comes dynamic sql
set #sql='select * from '#dbname+'dbo.yourtable';
Try to use dynamic sql
declare #ChosenDatabase varchar(100)='dbname'
DECLARE #SQL NVARCHAR(MAX)
SET #SQL= 'select * from '+ #ChosenDatabase+'.dbo.Customer'
--SELECT #SQL
EXEC SP_EXECUTESQL #SQL

Use Stored Procedure To Create View

I want to create a stored procedure which I can pass a parameter to for the database name and it will create a view for me.
I am just trying to save some time by not writing the same statement over and over and over for a create vew.
Below is my syntax - how could this be modified to run in a stored procedure accepting a parameter?
Alter View dbo.ForceClose
As
SELECT DISTINCT(SessionID) As CountofSessionID
FROM Database1
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN ('JJones', 'MHill', 'RMort'))
Go
And in the stored procedure accept a parameter as the database name (I know this isn't valid just trying to show an example) -- call the stored procedure like so
exec dbo.Procedure 'DBName'
And the stored procedure would then look like
#DBName varchar(100)
Select blah blah FROM' + #DBName + '
You mean this?
CREATE PROCEDURE dbo.ForceClose
(
#DBNAME NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #SQL AS NVARCHAR(255)
#SQL='SELECT DISTINCT(SessionID) As CountofSessionID
FROM ['+#DBNAME+'].[SCHEMA].[TABLE_NAME]
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN (''JJones'', ''MHill'', ''RMort''))'
EXEC (#SQL)
END
And you can run it by:
EXEC ForceClose <DBNAME>
With the View:
CREATE PROCEDURE dbo.ForceClose
(
#DBNAME NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #SQL AS NVARCHAR(255)
#SQL='CREATE VIEW VIEW_'+DBNAME+' AS SELECT DISTINCT(SessionID) As CountofSessionID
FROM ['+#DBNAME+'].[SCHEMA].[TABLE_NAME]
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN (''JJones'', ''MHill'', ''RMort''))'
EXEC (#SQL)
END
And you can run it by:
SELECT * FROM VIEW_<DBNAME>
Im not sure what the create view is doing; the basic premise you want is dynamic SQL (and there are lots of good questions and answers on the subject. A really simple answer would be
declare #myparameter nvarchar(100)= 'master'
declare #myquery nvarchar(1000)
select #myquery = 'select * from ' + #myparameter + '.dbo.sysdatabases'
select #myquery
exec sp_executeSQL #myquery
Running this returns a list of the databases on your server.
If you want to create a view; would you not need to know the table to query? The basic technique is the same
declare #myparameter nvarchar(100)= 'master'
declare #myquery nvarchar(1000)
select #myquery = 'Create View myschema.vw' + #myparameter + ' as select * from ' + #myparameter + '.dbo.sysdatabases'
select #myquery
exec sp_executeSQL #myquery
If this is in an application the user permissions to do this would be very high for a general application; you might want to wrap it with an execute as permission to stop people doing too much damage.

SQL - Can I pass a variable to be used in the FROM statement?

Can I pass a variable to a SELECT statement?
I keep getting an error message saying I need to declare it.
However, it is declared.
SELECT (list of columns)
FROM #database_table
You are looking to use Dynamic SQL to perform this type of query.
The Curse and Blessings of Dynamic SQL
Here is a quick sample
declare #sqlstatement nvarchar(4000)
declare #table sysname
set #table = 'yourTableName'
set #sqlstatement = 'SELECT * FROM ' + QUOTENAME(#table)
exec(#sqlstatement)
Yes, use dynamic sql statements to build your select statement.
-- Procedure input parameters
#TableName varchar(50)
-- Query guts
Declare #sql varchar(2000)
Set #sql = 'Select columnname from ' + #TableName
exec (#sql)
The one time you can do what you want is when you use table variables. You have to define the variables as:
declare #name table (<column list>)
This is alternative method of declaring a temporary table.
Other than this, I fully agree with bluefeet. You should read the link he posted.

Creating stored procedure in another database

Any idea if it's possible to create a procedure in another database using T-SQL alone, where the name of the database is not known up front and has to be read from a table? Kind of like this example:
Use [MasterDatabase]
Declare #FirstDatabase nvarchar(100)
Select Top 1 #FirstDatabase=[ChildDatabase] From [ChildDatabases]
Declare #SQL nvarchar(4000)
Declare #CRLF nvarchar(10) Set #CRLF=nchar(13)+nchar(10)
Set #SQL =
'Use [+'#Firstdatabase+']'+#CRLF+
'Go'+#CRLF+
'Create Proc [Test] As Select 123'
Exec (#SQL)
See what I'm trying to do? This example fails because Go is actually not a T-SQL command but it something recognised by the query analyser/SQL management studio and produces an error. Remove the Go and it also fails because Create Proc must be the first line of the script. Arrgg!!
The syntax of T-SQL doesn't allow you do things like this:
Create [OtherDatabase].[dbo].[Test]
Which is a shame as it would work a treat! You can do that with Select statements, shame it's inconsistent:
Select * From [OtherDatabase]..[TheTable]
Cheers, Rob.
It's a pain, but this is what I do. I took this from an example I found on sqlteam, I think - you might have some quoting issues with the way I did the indiscriminate REPLACE:
DECLARE #sql AS varchar(MAX)
DECLARE #metasql as varchar(MAX)
DECLARE #PrintQuery AS bit
DECLARE #ExecQuery AS bit
SET #PrintQuery = 1
SET #ExecQuery = 0
SET #sql =
'
CREATE PROCEDURE etc.
AS
BEGIN
END
'
SET #metasql = '
USE OtherDatabase
EXEC (''' + REPLACE(#sql, '''', '''''') + ''')
'
IF #PrintQuery = 1
PRINT #metasql
IF #ExecQuery = 1
EXEC (#metasql)
DECLARE #UseAndExecStatment nvarchar(4000),
#SQLString nvarchar(4000)
SET #UseAndExecStatment = 'use ' + #DBName +' exec sp_executesql #SQLString'
SET #SQLString = N'CREATE Procedure [Test] As Select 123'
EXEC sp_executesql #UseAndExecStatment,
N'#SQLString nvarchar(4000)', #SQLString=#SQLString
This is how i have done with Alter Procedure:
DECLARE #metasql as varchar(MAX)
DECLARE #sql AS varchar(MAX)
SET #sql =
'ALTER PROCEDURE [dbo].[GetVersion]
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP(1)[Version] from VersionTable
END'
SET #metasql = '
USE MyProdDb
IF (OBJECT_ID(''GetVersion'') IS NOT NULL OR OBJECT_ID(''GetVersion'', ''P'') IS NOT NULL)
BEGIN
EXEC (''' + REPLACE(#sql, '''', '''''') + ''')
END
'
--PRINT #metasql
EXEC (#metasql)
You could shell out to osql using xp_cmdshell, I suppose.
I dont think this can be done with TSQL.
You could use an SSIS package that looped the names and connected to the servers dynamically which creates the schema (procs ) you need.
This is probably what I would do as it means it is all contained within the package.
Configuration can be kept separate by either using a table or external xml file that contained the list of server/databases to deploy the schema to.
It's not necessary to use EXEC within EXEC.
You can simply use OtherDatabase.sys.sp_executesql
DECLARE #sql AS varchar(MAX) = N'
CREATE PROCEDURE etc.
AS
BEGIN
-- whatever
END
';
PRINT #sql;
EXEC OtherDatabase.sys.sp_executesql #sql;

Resources