Stored procedure to Linked server with parameters - Error - sql

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

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';
...

Invalid column name error when using QUOTENAME

I have several tables having the same structure. The tables are named by year that is 2001,2002 and so on. I am in need to search a column for a value in each table and get the count for each table.
I have created a stored procedure below but I keep getting an error
Invalid column 'lol'
This is the stored procedure used:
CREATE PROCEDURE [dbo].[CountSP]
#TableName NVARCHAR(128),
#SearchParam NVARCHAR(50),
#SearchInput NVARCHAR(200)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT COUNT('+QUOTENAME(#SearchParam)+') FROM ' + QUOTENAME(#TableName) +'WHERE'+QUOTENAME(#SearchParam)+'LIKE '+QUOTENAME(#SearchInput)+
+ N' SELECT * FROM '+QUOTENAME(#TableName)
EXECUTE sp_executesql #Sql
END
Executing it:
DECLARE #return_value INT
EXEC #return_value = [dbo].[CountSP]
#TableName = N'1999',
#SearchParam = N'USERDESC',
#SearchInput = N'lol'
SELECT 'Return Value' = #return_value
I don't know why you are using LIKE operator there while you don't use wildcards, also use SysName datatype directly for object names.
Create PROCEDURE [dbo].[CountSP]
(
#TableName SysName,
#SearchInput NVARCHAR(50),
#SearchParam SysName
)
AS
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX) = N'SELECT COUNT(' +
QUOTENAME(#SearchParam) +
N') FROM ' +
QUOTENAME(#TableName) +
N' WHERE ' +
QUOTENAME(#SearchParam) +
N' = ' + --You can change it to LIKE if needed
QUOTENAME(#SearchInput, '''') +
N';';
-- There is no benifits of using LIKE operator there
EXEC sp_executesql #SQL;
Then you can call it as
EXEC [dbo].[CountSP] N'YourTableNameHere', N'SearchInput', N'ColumnName';
This is because it is currently translated to :
SELECT COUNT([USERDESC]) FROM [1999] WHERE [USERDESC] LIKE [lol]
this means that it is comparing the "USERDESC" column with the "lol" column but from what I am understanding lol isn't a column but a value? which means you should lose the QUOTENAME for that variable.
See the documentation here : https://learn.microsoft.com/en-us/sql/t-sql/functions/quotename-transact-sql?view=sql-server-2017
You need to pass your parameter #SearchInput as a parameter to sp_execute:
CREATE PROCEDURE [dbo].[CountSP] #TableName sysname, --This is effectively the same datatype (as sysname is a synonym for nvarchar(128))
#SearchParam sysname, --Have changed this one though
#SearchInput nvarchar(200)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql nvarchar(MAX);
SET #Sql = N'SELECT COUNT(' + QUOTENAME(#SearchParam) + N') FROM ' + QUOTENAME(#TableName) + N'WHERE' + QUOTENAME(#SearchParam) + N' LIKE #SearchInput;' + NCHAR(13) + NCHAR(10) +
N'SELECT * FROM ' + QUOTENAME(#TableName);
EXECUTE sp_executesql #SQL, N'#SearchInput nvarchar(200)', #SearchInput;
END;
QUOTENAME, by default, will quote a value in brackets ([]). It does accept a second parameter which can be used to define a different character (for example QUOTENAME(#Value,'()') will wrap the value in parentheses). For what you want though, you want to parametrise the value, not inject (a quoted) value.

SQL Pass a string to a Stored Procedure that is not a Column Name

Can I pass a String to a Stored Procedure that is not a Column Name?
I need to call the StoredProcedure from C#.
The following does not work as the parameter can't be defined without it's Type, but shows what I am trying to do. Problem is that Sql is looking at #stringToIdentifyDataTable as a ColumnName, which seems fair, but not what I am trying to do.
Alter PROCEDURE [dbo].[PutNewTypeSource] #stringToIdentifyDataTable,
#ID int, #Description varchar(50), #Active bit
AS
DECLARE
#Query AS VARCHAR(MAX),
#Field_Out AS VARCHAR(MAX)
SELECT #Field_Out = CASE #stringToIdentifyDataTable
WHEN 'ReferralSource' THEN '[Adm].[JobReferralSource]'
WHEN 'ReferralSource2' THEN '[Adm].[JobReferralSource2]'
END
SET #Query = concat('
IF EXISTS (SELECT ID FROM ',#Field_Out,' WHERE Description= ',#Description,')
BEGIN
UPDATE ',#Field_Out,
'SET Active = ',#Active,
'WHERE Description= ',#Description,';
END')
EXEC (#Query)
exec [PutNewTypeSource] 'ReferralSource', 1, 'Description1', 0
If I understand correctly what you could do is this. note that I properly quote your object, and importantly parametrise you parameters. What you have before was wide open to injection:
ALTER PROCEDURE [dbo].[PutNewTypeSource] #Referral varchar(50), #Description varchar(50), #Active bit --I remvoed #ID as it was never used
AS BEGIN
DECLARE #Schema sysname,
#Table sysname;
SET #Schema = CASE WHEN #Referral IN ('ReferralSource','ReferralSource2') THEN N'adm' END;
SET #Table = CASE #Referral WHEN 'ReferralSource' THEN N'JobReferralSource'
WHEN 'ReferralSource2' THEN N'JobReferralSource2' END;
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'UPDATE ' + QUOTENAME(#Schema) + N'.' + QUOTENAME(#Table) + NCHAR(13) + NCHAR(10) +
N'SET Active = #Active' + NCHAR(13) + NCHAR(10) +
N'WHERE Description= #Description;';
EXEC sp_executesql #SQL, N'#Description varchar(50), #Active bit', #Description = #Description, #Active = #Active;
END;

Db Name as parameter in stored procedure SQL Server

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';
...

SQL Linked Server query with Parameters

I need to select value from SQL Linked Server & get it to loacal variable
This is what I've written so far:
DECLARE #SQLQUERY AS VARCHAR(1000)
DECLARE #FINALQUERY AS VARCHAR(1000)
DECLARE #OutVal AS VARCHAR(10)
SET #SQLQUERY = 'SELECT Field1 FROM Table1 WHERE Field2=' + CAST(#var1 AS VARCHAR)
SET #FINALQUERY = 'SELECT #OutVal=Field1 FROM OPENQUERY(LINKEDSERVER,' + '''' + #SQLQUERY + '''' + ')'
EXEC(#finalQuery)
but this is wrong as it does not set the local variable(#OutVal).
Instead of exec, use sp_execute_sql with an output parameter:
exec sp_executesql #FinalQuery, N'#OutVal output', #OutVal = #OutVal out
Since sp_executesql expects nvarchar parameters, be sure to change the definition of #FinalQuery to nvarchar(max).
#OutVal in query string does not recognized as a variable. use a function or return table statement.