Db Name as parameter in stored procedure SQL Server - sql

I'm looking to pass my database name as a parameter to the stored procedure, and I'm looking to use it in the where condition to set the database of the stored procedure. But I get an error:
Incorrect syntax near '.'
Sample Code
Create proc [dbo].[stored_procedure_one]
#variable1 int,
#dbname varchar(10)
as
begin
select *
from #dbname..table_name
End
Can someone suggest me how to solve this?

You will need to use dynamic sql for this something like this.....
Create proc [dbo].[stored_procedure_one]
#variable1 int,
#dbname SYSNAME --<-- use appropriate data type for object names
as
begin
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' select * from ' + QUOTENAME(#dbname) + N'..table_name'
Exec sp_executesql #Sql
End
Also use QUOTENAME() function to protect yourself against possible sql-injection attack.

Just to offer an alternative, it's fun to note that EXEC can take a string as the thing to execute, so for example:
DECLARE #sp nvarchar(255) = N'sys.sp_who2';
EXEC #sp;
It can also take parameters, e.g.
DECLARE #sp nvarchar(255) = N'sys.sp_who2';
EXEC #sp 'active';
So we can dynamically build the context where we run a command by using:
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
DECLARE #sql nvarchar(max) = N'SELECT DB_NAME();';
EXEC #context #sql;
And you can pass parameters, too:
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
DECLARE #sql nvarchar(max) = N'SELECT DB_NAME(), #x;';
EXEC #context #sql, N'#x int', 5;
This approach really simplifies things like concatenating the database name all over the place, avoiding db-specific functions like object_name, and ensures that your entire command runs in that other database. You can also do it across linked servers, e.g.:
DECLARE #server sysname = N'linked_server';
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#server)
+ N'.' + QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
...

Related

Can I create a stored procedure to select a table from a database with the database name as a variable? [duplicate]

I'm looking to pass my database name as a parameter to the stored procedure, and I'm looking to use it in the where condition to set the database of the stored procedure. But I get an error:
Incorrect syntax near '.'
Sample Code
Create proc [dbo].[stored_procedure_one]
#variable1 int,
#dbname varchar(10)
as
begin
select *
from #dbname..table_name
End
Can someone suggest me how to solve this?
You will need to use dynamic sql for this something like this.....
Create proc [dbo].[stored_procedure_one]
#variable1 int,
#dbname SYSNAME --<-- use appropriate data type for object names
as
begin
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' select * from ' + QUOTENAME(#dbname) + N'..table_name'
Exec sp_executesql #Sql
End
Also use QUOTENAME() function to protect yourself against possible sql-injection attack.
Just to offer an alternative, it's fun to note that EXEC can take a string as the thing to execute, so for example:
DECLARE #sp nvarchar(255) = N'sys.sp_who2';
EXEC #sp;
It can also take parameters, e.g.
DECLARE #sp nvarchar(255) = N'sys.sp_who2';
EXEC #sp 'active';
So we can dynamically build the context where we run a command by using:
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
DECLARE #sql nvarchar(max) = N'SELECT DB_NAME();';
EXEC #context #sql;
And you can pass parameters, too:
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
DECLARE #sql nvarchar(max) = N'SELECT DB_NAME(), #x;';
EXEC #context #sql, N'#x int', 5;
This approach really simplifies things like concatenating the database name all over the place, avoiding db-specific functions like object_name, and ensures that your entire command runs in that other database. You can also do it across linked servers, e.g.:
DECLARE #server sysname = N'linked_server';
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#server)
+ N'.' + QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
...

Is there a way to pass in a variable into a stored procedure when using dynamic SQL?

DECLARE #DBNAME nvarchar(200) = 'MY_DB_NAME',
#STOREDPROC nvarchar(200) = 'dbo.Friends_SelectById',
#Id int = 2
EXEC [dbo].[Test_WithID_Proc]
#DBNAME,
#STOREDPROC,
#Id
DECLARE #SQL_QUERY NVARCHAR(4000)
SET #SQL_QUERY = N'EXEC ' + #DBNAME + N'.' + #STOREDPROC + #Id
EXECUTE sp_executesql #SQL_QUERY, N'#Id int', #Id
This won't run of course but it's what I had in mind of dynamically passing in the variable that the stored procedure requires.
Context: I'm dynamically calling another DB with a particular stored procedure name. I've plans to call 100+ other DBs for testing purposes. Procedures that don't require any input I call the same way
You don't actually need dynamic SQL at all here.
SQL Server supports using a variable for the procedure name in an EXEC. So you can do this:
CREATE OR ALTER PROCEDURE Test_WithID_Proc
#DBNAME sysname,
#SCHEMANAME sysname,
#STOREDPROC sysname,
#Id int
AS
DECLARE #ProcedureName nvarchar(776) = QUOTENAME(#DBName) + N'.' + QUOTENAME(#SCHEMANAME) + N'.' + QUOTENAME(#STOREDPROC);
EXEC #ProcedureName
#Id = #Id;
GO
DECLARE #DBNAME sysname = N'MY_DB_NAME'
,#SCHEMANAME sysname = N'dbo'
,#STOREDPROC sysname = N'Friends_SelectById'
,#Id int = 2;
EXEC [dbo].[Test_WithID_Proc]
#DBNAME,
#STOREDPROC,
#SCHEMANAME,
#Id;
You're concatenating your variable's value, but then passing it as a parameter. Also the syntax for objects is {Database Name}.{Schema Name}.{Object Name}, you have omitted the schema's name.
I suspect you want something like this:
DECLARE #DBName sysname = N'YourDatabase',
#ProcedureName sysname = N'YourProcedure',
#Id int = 1;
DECLARE #SQL nvarchar(MAX) = N'EXEC ' + QUOTENAME(#DBName) + N'.dbo.' + QUOTENAME(#ProcedureName) + N' #Id;';
EXEC sys.sp_executesql #SQL, N'#Id int', #Id;

Stored procedure to Linked server with parameters - Error

I'm trying to create stored procedure to Linked server, which input parameter #ServerName is the name of Linked Server i use.
In this procedure I also Declare parameter which value I want to get from Dynamic SQL Query and line.
CREATE PROC sp_Version #ServerName varchar(30)
as
Declare #Ver varchar(10)
exec ('select #Ver from openquery(' + #ServerName + ', ''SELECT SUBSTRING (##VERSION, 22, 7) = #Ver''')
When I execute my sp i get an error saying:
"Must declare the scalar variable "#Ver"."
Could you please help me?
I'm not sure what your aim is with the value of #Ver, perhaps an OUTPUT parameter? If so, then the syntax would be:
CREATE PROC GetVersion #ServerName varchar(30), #Ver nvarchar(500) OUTPUT AS
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SELECT #dVer = Version' + NCHAR(10) +
N'FROM OPENROWSET(''SQLNCLI'',' + NCHAR(10) +
N' ' + QUOTENAME('Server=' + #ServerName + ';Trusted_Connection=YES;','''') + ',' +NCHAR(10) +
N' ''SELECT ##VERSION AS Version'');';
PRINT #SQL;
EXEC sp_executesql #SQL, N'#dVer nvarchar(500) OUTPUT', #dVer = #Ver OUTPUT;
GO
DECLARE #ver varchar(500)
EXEC GetVersion 'YourServerName', #ver OUTPUT;
PRINT #ver;
GO
DROP PROC GetVersion;
Note, firstly, as suggested I didn't use the sp_ prefix. I've also used sp_executesql instead of simply EXEC (this is generally better practice, as you can parametrise your dynamic SQL then, as i have done), and QUOTENAME to try and avoid injection.
I have come across this situation a couple of times. Try this:
CREATE PROC sp_Version #ServerName varchar(30)
as
Declare #Ver varchar(10)
DECLARE #SqlCommand nvarchar(MAX)
SET #SqlCommand = 'SELECT #Ver2 = SUBSTRING (##VERSION, 22, 7) '
DECLARE #sp_executesql VARCHAR(100)
SET #sp_executesql = #ServerName + '.master.sys.sp_executesql'
EXEC #sp_executesql #SqlCommand, N'#Ver2 nvarchar(10) out', #Ver out
SELECT #Ver

Dynamic SQL Server stored procedure with table name as input param

I have the following stored procedure
CREATE PROCEDURE [dbo].[Insert]
#Service varchar(max),
#TableName varchar(100),
#Query varchar(250),
#Results varchar(max),
#CreatedDate datetime,
#ExpirationDate datetime
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION
DECLARE #SQL NVARCHAR(MAX), #ParmDefinition NVARCHAR(MAX)
DECLARE #q1 VARCHAR(MAX), #rez1 VARCHAR(MAX),
#date1 DATETIME, #date2 DATETIME
DECLARE #tablename VARCHAR(MAX) = #Service + '.' + #TableName
SET #SQL = N'if not EXISTS (select #q from ' + #tablename + ' where Query = #q) insert into ' + #tablename + ' values(#q, #rez, #date1, #date2)'
SET #ParmDefinition = N'#q varchar(max), #rez varchar(max),
#date1 datetime, #date2 datetime'
EXECUTE sp_executeSQL -- Dynamic T-SQL
#SQL,
#ParmDefinition,
#q = #Query,
#rez = #Results,
#date1= #CreatedDate,
#date2 = #ExpirationDate
COMMIT TRANSACTION
END
When I try to execute it, it doesn't insert anything, 0 rows
If I execute the code without the stored procedure, like a single query it inserts
Am I missing something?
There are a lot of things you have done in your question which doesnt make any sense to me, Why do you need to declare all these variables inside your procedure.
Yes true you are using parametrised query to protect yourself against sql injection attack, yet you left a hole by concatenating the object names (Table and database name), yes you will need to concatenate them but you can use QUOTENAME() function around them passed parameters and enforce sql server to put square brackets around these parameters and force sql server to treat them as nothing else but object names.
And Selecting a variable in IF EXISTS not make much sense. Select 1 which returns true if a row is found with matching criteria , and if no row is found it will simply insert a row.
Only declare variables that needs to declared, otherwise this make it look like a mess and difficult to debug. As they say Keep it simple :)
Also use appropriate data types for your parameters, #Service I believe is your database name why does it need to be a VARCHAR(MAX) data type, use the data type specific to store Sql Server Object names SYSNAME.
CREATE PROCEDURE [dbo].[Insert]
#Service SYSNAME,
#TableName SYSNAME,
#Query varchar(250),
#Results varchar(max),
#CreatedDate datetime,
#ExpirationDate datetime
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION
DECLARE #SQL NVARCHAR(MAX), #ParmDefinition NVARCHAR(MAX)
SET #SQL = N'IF NOT EXISTS (select 1 from ' + QUOTENAME(#Service) + '.' + QUOTENAME(#TableName)
+ N' where Query = #q) '
+ N'insert into ' + QUOTENAME(#Service) + '.' + QUOTENAME(#TableName)
+ N' values(#q, #rez, #date1, #date2)'
SET #ParmDefinition = N'#q varchar(250), #rez varchar(max),
#date1 datetime, #date2 datetime'
EXECUTE sp_executeSQL #SQL
,#ParmDefinition
,#q = #Query
,#rez = #Results
,#date1= #CreatedDate
,#date2 = #ExpirationDate
COMMIT TRANSACTION
END

Selecting from a table where the name is passed as a variable

I am trying to write a simple stored proc which takes three arguments 'database name one', 'database name two' and 'table name'. The sql will then perform a row count for the defined table in each database and store it.
Working on it piecemeal I have hit the first problem in that you can't do
select * from #tablename
I know you can use dynamic sql with the exec command but this is not ideal as I can't return values.
The following example looks like it should work but doesn't.
declare #tablename as nvarchar(500)
declare #sqlstring as nvarchar(500)
declare #parmdefinition as nvarchar(500)
declare #numrows as bigint
set #tablename = N'dummy_customer'
set #parmdefinition = N'#tablenameIN nvarchar(500), #numrowsOUT as bigint OUTPUT'
select #sqlstring = 'select #numrowsOUT = count(*) from #tablenameIN'
select #sqlstring
exec sp_executesql #sqlstring, #parmdefinition, #tablenameIN = #tablename, #numrowsOUT = #numrows OUTPUT
select #numrows
The error message given is
Msg 1087, Level 16, State 1, Line 1
Must declare the table variable "#tablenameIN".
Currently using SQL Server 2008 SP2.
Edit:
We're doing this because we are doing a migration and the customer wants a report which shows the row count for each table in the source and destination database. As there are many tables being able to use sp_MSForEachTable to call the stored proc seems ideal.
Edit:
The final solution for future reference is
declare #tablename as nvarchar(500)
declare #sqlstring as nvarchar(500)
declare #parmdefinition as nvarchar(500)
declare #numrows as bigint
set #tablename = N'dummy_customers'
set #parmdefinition = N'#tablename nvarchar(500), #numrowsOUT as bigint OUTPUT'
select #sqlstring = 'select #numrowsOUT = count(*) from ' + quotename(#tablename)
exec sp_executesql #sqlstring, #parmdefinition, #tablename = #tablename, #numrowsOUT = #numrows OUTPUT
select #numrows
You'd have to use dynamic sql, and concatenate the table name into the SQL string to then execute via sp_executsql:
select #sqlstring = 'select #numrowsOUT = count(*) from ' + QUOTENAME(#tablename)
EXECUTE sp_executesql ....