Set database name dynamically in SQL Server stored procedure? - sql-server

How do I set the database name dynamically in a SQL Server stored procedure?

Sometimes, the use of SYNONYMs is a good strategy:
CREATE SYNONYM [schema.]name FOR [[[linkedserver.]database.]schema.]name
Then, refer to the object by its synonym in your stored procedure.
Altering where the synonym points IS a matter of dynamic SQL, but then your main stored procedures can be totally dynamic SQL-free. Create a table to manage all the objects you need to reference, and a stored procedure that switches all the desired synonyms to the right context.
This functionality is only available in SQL Server 2005 and up.
This method will NOT be suitable for frequent switching or for situations where different connections need to use different databases. I use it for a database that occasionally moves around between servers (it can run in the prod database or on the replication database and they have different names). After restoring the database to its new home, I run my switcheroo SP on it and everything is working in about 8 seconds.

Stored Procedures are database specific. If you want to access data from another database dynamically, you are going to have to create dynamic SQL and execute it.
Declare #strSQL VarChar (MAX)
Declare #DatabaseNameParameter VarChar (100) = 'MyOtherDB'
SET #strSQL = 'SELECT * FROM ' + #DatabaseNameParameter + '.Schema.TableName'
You can use if clauses to set the #DatabaseNameParameter to the DB of your liking.
Execute the statement to get your results.

This is not dynamic SQL and works for stored procs
Declare #ThreePartName varchar (1000)
Declare #DatabaseNameParameter varchar (100)
SET #DatabaseNameParameter = 'MyOtherDB'
SET #ThreePartName = #DatabaseNameParameter + '.Schema.MyOtherSP'
EXEC #ThreePartName #p1, #p2... --Look! No brackets

Related

How to pass a table name as variable to a stored procedure

I have inherited a bunch of stored procedures basically as a shell and inside the quotes is this huge dynamic SQL with lots of conditions, calculations and case statements, however the table name in the FROM clause within this dynamic SQL changes every quarter.
Now before I get flamed, I like to simply say that I inherited them, how it was designed was before me. So each quarter when a call is made out to these stored procedures, it comes with the actual table name passed as a parameter and then the dynamic SQL concatenates the table name.
The problem with this approach is that, with each run over time, the prior designers simply tacked on more criteria as conditions and calculations. But the dynamic SQL string has a length limit to it. Further it becomes quite difficult to maintain and debug.
CREATE PROCEDURE .....
#dynSQL1 = 'SELECT......
FROM' + strTblName + '
WHERE.....
GROUP BY....'
...
EXEC #dynSQL1
GO
However, I like to ask you all, is there a way to turn this stored procedure with this huge dynamic SQL string into a plain vanilla stored procedure based on a parameterized table name?
My main goal is two fold, one, get away from the long string as dynamic SQL and two, easier maintenance and debugging. I would like to think in the more current version of SQL Server from SQL Server 2016/2017 and on, this issue is addressed.
Your thoughts and suggestions is greatly appreciated.
~G
So each quarter when a call is made out to these stored procedures, it comes with the actual table name passed as a parameter and then the dynamic SQL concatenates the table name.
You could change the procedure to codegen other stored procedures instead of running dynamic SQL. EG:
CREATE PROCEDURE admin.RegenerateProcedures #tableName
as
begin
declare #ddl nvarchar(max) = '
create or alter procedure dbo.SomeProc
as
begin
SELECT......
FROM dbo.' + quotename(#tableName) + '
WHERE.....
GROUP BY....
end
'
EXEC ( #ddl )
. . .
end
GO

Table-Based Function using variables and OpenQuery

I am attempting to query data from our Oracle server via our SQL server. To perform this in a thin-client manner, I am using OpenQuery. I would like to build a single table-based function that can be used to query various linked tables as needed. I can't quite figure out the syntax to make this work. Here is what I have so far. Any help is greatly appreciated!
CREATE FUNCTION [dbo].[fnTEST](
#Table varchar (100),
#Fields varchar (1000),
#Condition varchar(5000)
)
RETURNS
#TEST TABLE()
AS
BEGIN
DECLARE #OPENQUERY nvarchar(4000);
DECLARE #TSQL nvarchar(4000);
SET #OPENQUERY = 'SELECT * FROM OPENQUERY([TEST-Link],'''
SET #TSQL = 'SELECT ' + #Fields + ' FROM TEST.' + #Table + ' WHERE ' + #Condition + ''')'
EXEC (#OPENQUERY+#TSQL)
END;
The error I am currently getting is:
Msg 102, Level 15, State 1, Procedure fnTEST, Line 12 [Batch Start Line 7]
Incorrect syntax near ')'.
Highlighted at #TEST TABLE()
This is all not recommended for a number of reasons, but here the big one is that, as indicated in the MS doc, you cannot use dynamic SQL from a user-defined function:
Before You Begin
Limitations and restrictions
...
User-defined functions cannot make use of dynamic SQL or temp tables. Table variables are allowed.
...
Here are some of the other problems with this approach:
Your dynamic SQL is injectable. You should never use dynamic SQL unless you understand what SQL Injection is and how to prevent it in your dynamic SQL code.
Using dynamic sql has potential security requirements and restrictions. In this case the dynamic SQL may not have the same rights as your account and may not be able to use an OPENQUERY.
The nature of Database and Server Trustworthy settings may block this anyway.
IMHO, OPENQUERY is not recommended (some disagree), and remote queries are better handled with Linked Servers and the Remote EXEC command.
You are trying to write a "Universal Query" here. Universal Queries are generally not a good idea and have security problems, even after you fix the SQL Inject issues. It's better to define the specific queries needed by your app and code them as stored procedures and/or fixed queries using parameters only for WHERE conditions.
A SQL Function is not the right place for all of this anyway. You should regard a SQL table function as akin to a View, but with parameters for your WHERE clause. You should not treat it as a way to magically do anything.
The way that I would do something link this is as follows:
Define the explicit queries/datasets that your app needs from the Oracle Database.
Write those queries as stored procedures, on the Oracle database.
Setup a Linked Server definition in your SQL Server database to the Oracle database. Configure the security for each side appropriately.
Write specific stored procedures on your SQL Server to call the corresponding procedures in the Oracle database. Use remote EXEC's to do this through the Linked Server definition.
(NOTE: Remote EXEC execution is done with the AT <linkedServer> clause).
Enable the linked server for rpc out and simplify this to
EXEC (#sql) at [TEST-Link]

How to Auto Generate Code for Stored Procedure Column Data Types - SQL Server

My desired end result is to simply be able to SELECT from a Stored Procedure. I've searched the Internet and unfortunately the Internet said this can't be done and that you first need to create a Temp Table to store the data. My problem is that you must first define the columns in the Temp Table before Executing the STORED Procedure. This is just time consuming. I simply want to take the data from the stored procedure and just stick it into a Temp Table.
What is the FASTEST route to achieve this from a coding perspective? To put it simply it's time consuming to first have to lookup the returned fields from a Stored Procedure and then write them all out.
Is there some sort of tool that can just build the CREATE Table Statement based on the Stored Procedure? See screenshot for clarification.
Most of the Stored Procedures I'm dealing with have 50+ fields. I don't look forward to defining each of these fields manually.
Here is good SO Post that got me this far but not what I was hoping. This still takes too much time. What are experienced SQL Server guys doing? I've only just recently made the jump from Oracle to SQL Server and I see that Temp Tables are a big deal in SQL Server from what I can tell.
You have several options to ease your task. However, these won't be fully automatic. Be aware that these won't work if there's dynamic sql in the procedure's code. You might be able to format the result from the functions to increase the automation allowing you to copy and paste easily.
SELECT * FROM sys.dm_exec_describe_first_result_set_for_object(OBJECT_ID('report.MyStoredProcedureWithAnyColumns'), 0) ;
SELECT * FROM sys.dm_exec_describe_first_result_set(N'EXEC report.MyStoredProcedureWithAnyColumns', null, 0) ;
EXEC sp_describe_first_result_set #tsql = N'EXEC report.MyStoredProcedureWithAnyColumns';
GO
If you don't mind ##temp table and some dynamic SQL
NOTE: As Luis Cazares correctly pointed out... the ##temp runs the risk of collision due to concurrency concerns
Example
Declare #SQL varchar(max) = 'Exec [dbo].[prc-App-Lottery-Search] ''8117'''
Declare #temp varchar(500) = '##myTempTable'
Set #SQL = '
If Object_ID(''tempdb..'+#temp+''') Is Not NULL Drop Table '+#temp+';
Create Table '+#temp+' ('+stuff((Select concat(',',quotename(Name),' ',system_type_name)
From sys.dm_exec_describe_first_result_set(#SQL,null,null ) A
Order By column_ordinal
For XML Path ('')),1,1,'') +')
Insert '+#temp+' '+#SQL+'
'
Exec(#SQL)
Select * from ##myTempTable

Using variables in TSQL and keep formatting in SQL Server Management Studio

I'm creating some views with a lot of references to tables in another database.
At some point the other database needs to change.
I want to make it easy for the next developer to change the scripts to use another database.
This obviously work like it should:
CREATE VIEW ViewName
AS
SELECT *
FROM AnotherDatabase.SchemaName.TableName;
But when I do:
DECLARE #DB CHAR(100)
SET #DB = 'AnotherDatabase'
GO
CREATE VIEW ViewName
AS
SELECT *
FROM #DB.SchemaName.TableName;
I get the error:
Msg 137, Level 15, State 2, Procedure ViewName, Line 3
Must declare the scalar variable "#DB".
I could do something like:
DECLARE #SQL ...
SET #SQL = ' ... FROM ' + #DB + ' ... '
EXEC (#SQL)
But that goes against the purpose of making it easier for the next developer - because this dynamic SQL approach removed the formatting in SSMS.
So my question is: how do I make it easy for the next developer to maintain T-SQL code where he needs to swap out the database reference?
Notes:
I'm using SQL Server 2008 R2
The other database is on the same server.
Consider using SQLCMD variables. This will allow you to specify the actual database name at deployment time. SQL Server tools (SSMS, SQLCMD, SSDT) will replace the SQLCMD variable names with the assigned string values when the script is run. SQLCMD mode can be turned on for the current query windows from the menu option Query-->SQLCMD mode option.
:SETVAR OtherDatabaseName "AnotherDatabaseName"
CREATE VIEW ViewName AS
SELECT *
FROM $(OtherDatabaseName).SchemaName.TableName;
GO
This approach works best when SQL objects are kept under source control.
When you declare variables, they only live during the execution of the statement. You can not have a variable as part of your DDL. You could create a bunch of synonyms, but I consider that over doing it a bit.
The idea that your database names are going to change over time seems a bit out of the ordinary and conceivably one-time events. However, if you do still require to have the ability to quickly change over to point to a new database, you could consider creating a light utility directly in SQL to automatically generate the views to point to the new database.
An implementation may look something like this.
Assumptions
Assuming we have the below databases.
Assuming that you prefer to have the utility in SQL instead of building an application to manage it.
Code:
create database This;
create database That;
go
Configuration
Here I'm setting up some configuration tables. They will do two simple things:
Allow you to indicate the target database name for a particular configuration.
Allow you to define the DDL of the view. The idea is similar to Dan Guzman's idea, where the DDL is dynamically resolved using variables. However, this approach does not use the native SQLCMD mode and instead relies on dynamic SQL.
Here are the configuration tables.
use This;
create table dbo.SomeToolConfig (
ConfigId int identity(1, 1) primary key clustered,
TargetDatabaseName varchar(128) not null);
create table dbo.SomeToolConfigView (
ConfigId int not null
references SomeToolConfig(ConfigId),
ViewName varchar(128) not null,
Sql varchar(max) not null,
unique(ConfigId, ViewName));
Setting the Configuration
Next you set the configuration. In this case I'm setting the TargetDatabaseName to be That. The SQL that is being inserted into SomeToolConfigView is the DDL for the view. I'm using two variables, one {{ViewName}} and {{TargetDatabaseName}}. These variables are replaced with the configuration values.
insert SomeToolConfig (TargetDatabaseName)
values ('That');
insert SomeToolConfigView (ConfigId, ViewName, Sql)
values
(scope_identity(), 'dbo.my_objects', '
create view {{ViewName}}
as
select *
from {{TargetDatabaseName}}.sys.objects;'),
(scope_identity(), 'dbo.my_columns', '
create view {{ViewName}}
as
select *
from {{TargetDatabaseName}}.sys.columns;');
go
The tool
The tool is a stored procedure that takes a configuration identifier. Then based on that identifier if drops and recreates the views in the configuration.
The signature for the stored procedure may look something like this:
exec SomeTool #ConfigId;
Sorry -- I left out the implementation, because I have to scoot, but figured I would respond sooner than later.
Hope this helps.

SQL Server Dynamic Stored Procedure

I am having the stored procedure. For that i need to pass the Database name as the paramters from another application or another SP. I know the approach of dynamic SQL, something like,
Create procedure mysp(#dbname varchar(20))
as
begin
declare #sql varchar(max)
set #sql='select * from '+#dbname+'.dbo.table'
end
exec mysp 'mydb'
But i dont want the SQL statements as a string. Because in my SP, i have many Sql statements are coming (Not like this only SELECT statement). so can i use,
USE DatabaseName
inside the stored procedure, so that i can use the db name in the sql statements directly without making it as string. Or any other approach is there.
My requirements, only for db name, i dont want the entire the sql statement to be dynamic...
please help me out.
Thanks in advance.
You can add the USE instruction to the dynamic query you are creating. Then you can work with that database's tables and other objects without the qualifier (within the dynamic query):
Create procedure mysp(#dbname varchar(20))
as
begin
declare #sql varchar(max)
set #sql='use '+#dbname;
set #sql=#sql + ';select ... from dbo.table1';
set #sql=#sql + ';update dbo.table2...';
set #sql=#sql + ';insert into dbo.table3...';
...
exec(#sql);
end
exec mysp 'mydb'
However, while you can do that, it's not something that you should do, unless you really have to. You are probably trying to avoid creating the same procedure in different DBs, but you may be getting you other problems with this approach, or robbing yourself of some advantages you might otherwise have without resorting to dynamic queries in SPs.
No, USE isn't allowed in stored procedures, functions and triggers.
A stored procedure is supposed to be local to the database. To access another database, there is one way (as far as I know), and it's the one you used.

Resources