stored procedure receiving DB name to work with - sql

I am looking to write a stored procedure which received a database name along with other parameters, and the stored procedure needs to work on the Database which it received
any thoughts please

Something like the following should work, as long as correct permissions are setup:
CREATE PROCEDURE dbo.sptest
#DB VARCHAR(50)
AS
BEGIN
DECLARE #sqlstmt VARCHAR(MAX)
SET #sqlstmt='SELECT TOP 10 * FROM ' + #DB + '.dbo.YourTableName'
sp_executesql #sqlstmt
END
GO
As mentioned, be very careful when using dynamic SQL like this- only use with trusted sources because of the ability to wreck havoc on your DB. At a minimum, you should add some checking of the value of #DB passed in to make sure it matches a limited list of database names that it will work with.

Related

Customizable database names and TempDB

I have a lump of SQL that looks a little like this
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = '{FOO}')
BEGIN
EXECUTE ('CREATE DATABASE {FOO}')
ALTER DATABASE {FOO} SET AUTO_CLOSE OFF
END
{FOO} is replaced at runtime with the name of a user configurable database. The logic is that I don't want to create the database if it already exists.
If {FOO} is tempdb then I get a failure when the query runs
Option 'AUTO_CLOSE' cannot be set in database 'tempdb'.
My question is why do I get this failure? SELECT * FROM sys.databases WHERE name = 'tempdb' returns zero results so surely my whole BEGIN/END pair shouldn't run? Indeed, if I put a print statement between begin and end, I don't see any output.
My guess is that SQL Server is doing some kind of linting on the SQL to make sure I don't muck around with tempdb? I have solved the problem by using EXECUTE instead, but I'm a little confused why I have to!
Try ensuring both commands are separate and within dynamic SQL, then the change to tempdb won't be caught by the parser:
EXEC sp_executesql N'CREATE DATABASE {FOO};';
EXEC sp_executesql N'ALTER DATABASE {FOO} SET AUTO_CLOSE OFF;';
This is similar to the reason you can't do this:
IF 1 = 1
BEGIN
CREATE TABLE #t1(id INT);
END
ELSE
BEGIN
CREATE TABLE #t1(x NVARCHAR(255));
END
Even though you and I know that only one of those #t1 code paths will ever be reached, SQL Server presumes that both paths could be reached at runtime, and so complains at parse time.

sql2008 - Better than multiple replace statements?

Further to my previous post, I would like to copy dependent objects (such as views or procedures) to a 'static' database. However, schema names and other object prefixes are not the same between Production and Static databases...
[I've read Aaron Bertrand's articles on setting up an Audit database, but this is a little much for our needs at this time.]
After extracting the object definitions into a variable using some dynamic sql, I am running multiple replace statements for each change so that the views/procedures still run, pulling data from the Static database.
The reason for the replace statements is that the views/procedures have been created using differing naming conventions. Sometimes I find <dbname>.dbo.<objectname>, other times it's <dbname>..<objectname> or even just dbo.<objectname> !
Instead of using multiple replace statements as below (I feel this may grow quite large!), is there a better method? Would a table-driven approach (using a CURSOR) be wiser/wisest?
[Database/object names have been modified in the code below for simplicity]
declare #sql nvarchar(500), #parmdef nvarchar(500),
#dbname varchar(20), #objname varchar(255), #ObjDef varchar(max);
set #dbname = 'ProdC';
--declare cursor; get object name using cursor on dbo.ObjectsToUpdate
--[code removed for simplicity]
set #sql = N'USE '+quotename(#dbname) +'; ' ;
set #sql = #sql + N'SELECT #def=OBJECT_DEFINITION(OBJECT_ID(''dbo.'+#objname+ '''));'
set #parmdef = N'#def nvarchar(max) OUTPUT' ;
exec sp_executesql #sql, #parmdef, #def=#ObjDef OUTPUT;
--Carry out object definition replacements
set #ObjDef= replace(#ObjDef, 'CREATE VIEW [dbo].[', 'ALTER VIEW ['+#dbname+'].[');
set #ObjDef= replace(#ObjDef, 'Prod1.dbo.', #dbname+'.'); --replace Prod1 with #dbname
set #ObjDef= replace(#ObjDef, ' dbo.', ' '+#dbname+'.'); --replace all 'dbo.'
set #ObjDef= replace(#ObjDef, 'dbo.LookupTable1', #dbname+'.LookupTable1');
--[code removed for simplicity]
exec(#ObjDef);
--get next object name from cursor
--[remaining code removed for simplicity]
Many thanks in advance.
Another problem that you have is using OBJECT_DEFINITION. This returns only the first 4,000 characters of the object name.
The same problem exists using INFORMATION_SCHEMA.ROUTINES.
Check out this post for a discussion of the alternatives...
Dude, where's the rest of my procedure?
"However, schema names and other object prefixes are not the same between Production and Static databases"
That's your problem right there. Make them the same and your problem will go away. With SQL Server supporting multiple instances there should be no barrier to doing that.
#ben: the Static database pull from several Production databases and there is a desire to maintain the original 'source' by using the database name as schema name in the Static database.
Closed. Duplicate with this post

T-SQL Dynamically execute stored procedure

I have a logging function in T-SQl similiar to this:
CREATE PROCEDURE [logging]
#PROCEDURE VARCHAR(50),
#MESSAGE VARCHAR(MAX)
AS
BEGIN
PRINT #MESSAGE
END;
GO
I am able to call it like this:
execute logging N'procedure_i_am_in', N'log_message';
As my stored procedure names are a bit long winded, I want to write an alias or an inline function or so, to call the logging procedure for me, with the current procedure. Something like this (which is broken):
declare #_log varchar(max)
set #_log = 'execute logging N''procedure_i_am_in'', '
execute #_log N'MESSAGE!'
And i would put that alias at the top of each procedure.
What are your thoughts?
Quite simple
CREATE PROCEDURE [logging]
#PROCID int,,
#MESSAGE VARCHAR(MAX)
-- allows resolution of #PROCID in some circumstances
-- eg nested calls, no direct permission on inner proc
WITH EXECUTE AS OWNER
AS
BEGIN
-- you are using schemas, right?
PRINT OBJECT_SCHEMA_NAME(#PROCID) + '.' + OBJECT_NAME(#PROCID);
PRINT #MESSAGE
END;
GO
Then
execute logging ##PROCID, N'log_message';
MSDN on OBJECT_SCHEMA_NAME and ##PROCID
Edit:
Beware of logging into tables during transactions. On rollback, you'll lose the log data
More trouble than it's worth, but
it would be
Set #_log = 'exec ....N' + 'MESSAGE!'
Exec (#log)
So not a lot of use.
Personally I'sd just rename the SP, or at a push use a tersely named function. Building strings and exec'ing them is an only if you must admin style facility IMHO

Dynamic View name in Table valued function

I'm passing View name as parameter in a Table Valued Function, and I want to fetch some data from that view by building a dynamic SQL and executing it by sp_executesql().
when try to execute the function, I get the error:
Only functions and extended stored procedures can be executed from within a function.
DBMS: SQL Server 2005
any workarounds?
set #SQLString =
N'select #Desc = Description from '
+ #TableName
+ ' where Code = #Code;'
execute sp_executesql #SQLString,
N'#Code nvarchar(500),
#Desc nvarchar(500) OUTPUT',
#Code = #Code,
#Desc=#Desc OUTPUT;
Well, you could wrap the dynamic SQL in an extended stored procedure. That would work, but I'd (strongly) advise against doing it.
SQL Server requires user-defined functions to be deterministic (with the exception of the aforementioned extended stored procedures) -- i.e. the results of the function should be uniformly predictable from the input parameters. Since stored procedures can access data from anywhere, use random numbers, etc., SQL Server will not allow you to use them inside a function.
There are other approaches you can use, such as prepopulating a table variable with your data, modifying your schema, and so forth, that will depend on your performance requirements and how you have the schema set up.
no unless you want to do a loopback query by calling an extended proc like xp_cmdshell
something like this, modify to fit your needs
CREATE FUNCTION fnBla(#id int)
RETURNS int
AS
BEGIN
DECLARE #SQL varchar(500)
SELECT #SQL='osql -S' +##servername +' -E -q "exec tempdb..prLog ''fnBla''"'
EXEC master..xp_cmdshell #SQL
RETURN #id
END
Just so that you know I would not do this this way since you are creating a loopback query and not executing the safest code
any reason you can't use a proc instead of a function?

How do I run SQL queries on different databases dynamically?

I have a sql server stored procedure that I use to backup data from our database before doing an upgrade, and I'd really like it to be able to run the stored procedure on multiple databases by passing in the database name as a parameter. Is there an easy way to do this? The best I can figure is to dynamically build the sql in the stored procedure, but that feels like its the wrong way to do it.
build a procedure to back up the current database, whatever it is. Install this procedure on all databases that you want to backup.
Write another procedure that will launch the backups. This will depend on things that you have not mentioned, like if you have a table containing the names of each database to backup or something like that. Basically all you need to do is loop over the database names and build a string like:
SET #ProcessQueryString=
'EXEC '+DatabaseServer+'.'+DatabaseName+'.dbo.'+'BackupProcedureName param1, param2'
and then just:
EXEC (#ProcessQueryString)
to run it remotely.
There isn't any other way to do this. Dynamic SQL is the only way; if you've got strict controls over DB names and who's running it, then you're okay just truncating everything together, but if there's any doubt use QUOTENAME to escape the parameter safely:
CREATE PROCEDURE doStuff
#dbName NVARCHAR(50)
AS
DECLARE #sql NVARCHAR(1000)
SET #sql = 'SELECT stuff FROM ' + QUOTENAME(#dbName) + '..TableName WHERE stuff = otherstuff'
EXEC sp_ExecuteSQL (#sql)
Obviously, if there's anything more being passed through then you'll want to double-check any other input, and potentially use parameterised dynamic SQL, for example:
CREATE PROCEDURE doStuff
#dbName NVARCHAR(50)
#someValue NVARCHAR(10)
AS
DECLARE #sql NVARCHAR(1000)
SET #sql = 'SELECT stuff FROM ' + QUOTENAME(#dbName) + '..TableName WHERE stuff = #pOtherStuff'
EXEC sp_ExecuteSQL (#sql, '#pOtherStuff NVARCHAR(10)', #someValue)
This then makes sure that parameters for the dynamic SQL are passed through safely and the chances for injection attacks are reduced. It also improves the chances that the execution plan associated with the query will get reused.
personally, i just use a batch file and shell to sqlcmd for things like this. otherwise, building the sql in a stored proc (like you said) would work just fine. not sure why it would be "wrong" to do that.
best regards,
don
MSSQL has an OPENQUERY(dbname,statement) function where if the the server is linked, you specify it as the first parameter and it fires the statement against that server.
you could generate this openquery statement in a dynamic proc. and either it could fire the backup proc on each server, or you could execute the statement directly.
Do you use SSIS? If so you could try creating a couple ssis packages and try scheduling them,or executing them remotely.