Passing parameters to a OPENQUERY - sql-server

Why is the following query does not work? It gives me error: Incorrect syntax near '+'.
SELECT *
INTO #tmpTable
FROM OPENQUERY("127.0.0.1", 'EXEC [DB].dbo.SP_inventory' + #StoreId + ',' + #StartDate ',' + #EndDate)
How should I be passing the parameters #StoreId #StartDate and #EndDate to make it work correctly? Thanks.

OPENQUERY requires a literal; it can't be an expression. If you need to pass parameters. one method is using dynamic SQL, but it can get "ugly". This is incomplete, as what we have is however
DECLARE #StoreId int = 7,
#StartDate date = '20190101',
#EndDate date = '20190701';
--Values shoukd be set
DECLARE #SQL nvarchar(MAX);
DECLARE #CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'{SELECT Statement parts}' + #CRLF +
N'FROM OPENQUERY("172.16.111.11", N''EXEC [DB].dbo.SP_inventory' + CONVERT(varchar(10),#StoreId) + ',' + QUOTENAME(CONVERT(varchar,#StartDate,112),'''') + ',' + QUOTENAME(CONVERT(varchar,#EndDate,112),'''') +') OQ';
PRINT #SQL; --Your best Friend
EXEC sp_executesql #SQL;
Therefore an alternative methhod is using EXECUTE ... AT, which requires a linked server:
EXEC (N'[DB].dbo.SP_inventory ?, ?, ?;',#StoreId, #StartDate, #EndDate) AT [{Linked Server Name}];

Related

What am I missing to execute stored procedure in OpenQuery

I am trying to use OpenQuery to insert data into a temporary table. OpenQuery suppose to execute a stored procedure that outputs hundreds of rows.
In order to not to defining Create Table #temp1, I am trying to execute OpenQuery and inserting the result into #temp1 table using into statement in Select query. I would like to know what is causing it not to execute?
It seems like it may quotes issue which is not placed correctly. How can I get help related to this?
My server contains multiple databases (instances). How do I need to define it here?
I am providing an example here of my query here
DECLARE #startDate DATETIME = CONVERT(DATE, GETDATE());
DECLARE #endDate DATETIME = CONVERT(DATE, GETDATE()+1);
DECLARE #stDate NVARCHAR(MAX) = CHAR(39) + CONVERT(NVARCHAR(255), #startDate, 23)+ CHAR(39) + ',';
DECLARE #enDate NVARCHAR(MAX) = CHAR(39) + CONVERT(NVARCHAR(255), #endDate, 23) + CHAR(39);
DECLARE #execCommand NVARCHAR(MAX) = CHAR(39) + 'EXEC dbo.getRecordsByOrderDate ' ;
DECLARE #concatCommand NVARCHAR(MAX) = #execCommand + #stDate + #enDate + CHAR(39);
DECLARE #opQuery Nvarchar(MAX) = 'Select * Into #Temp1 from Openquery(LOCALSERVER, '+ #concateCommand +') oq'
EXEC (#opQuery);
Error Message:
Msg 102, Level 15, State 1, Line 20 Incorrect syntax near '2020'.
If I try to execute in below format
Select * Into #Temp1 from Openquery(LOCALSERVER, 'EXEC dbo.getRecordsByOrderDate ''2020-12-11'',''''2020-12-12''''') oq
Msg 11529, Level 16, State 1, Procedure
sys.sp_describe_first_result_set, Line 1 [Batch Start Line 31] The
metadata could not be determined because every code path results in an
error; see previous errors for some of these.
Msg 2812, Level 16,
State 62, Procedure sys.sp_describe_first_result_set, Line 1 [Batch
Start Line 31] Could not find stored procedure
'dbo.getRecordsByOrderDate'.
The problem is your lack of escapting the quotes on your injected parameters. The simplest way to debug dynamic code is to PRINT/SELECT it. If you do that you'll quickly see the problem:
DECLARE #startDate DATETIME = CONVERT(DATE, GETDATE());
DECLARE #endDate DATETIME = CONVERT(DATE, GETDATE()+1);
DECLARE #stDate NVARCHAR(MAX) = CHAR(39) + CONVERT(NVARCHAR(255), #startDate, 23)+ CHAR(39) + ',';
DECLARE #enDate NVARCHAR(MAX) = CHAR(39) + CONVERT(NVARCHAR(255), #endDate, 23) + CHAR(39);
DECLARE #execCommand NVARCHAR(MAX) = CHAR(39) + 'EXEC dbo.getRecordsByOrderDate ' ;
DECLARE #concatCommand NVARCHAR(MAX) = #execCommand + #stDate + #enDate + CHAR(39);
DECLARE #opQuery Nvarchar(MAX) = 'Select * Into #Temp1 from Openquery(LOCALSERVER, '+ #concatCommand +') oq'
PRINT #opQuery;
Which returns...
Select *
Into #Temp1
from Openquery(LOCALSERVER, 'EXEC dbo.getRecordsByOrderDate '2020-12-11','2020-12-12'') oq
Voila your first parameter escapes the second parameter, and thus the error. (I Have added additional lines, as SO's choice of "prettifier" thinks # is a comment character in SQL, and I want the escaping to be highlighted in the colouring.)
When using single quotes inside a literal string, you need to escape them by "doubly them up" (''). Thus the final statement needs to me the below:
Select *
Into #Temp1
from Openquery(LOCALSERVER, 'EXEC dbo.getRecordsByOrderDate ''20201211'',''20201212''') oq
(Additional lines added for presentation again.)
So you need to fix the definitions of your 2 injected values:
DECLARE #stDate NVARCHAR(8) = CHAR(39) + CHAR(39) + CONVERT(NVARCHAR(8), #startDate, 112)+ CHAR(39) + CHAR(39) + ',';
DECLARE #enDate NVARCHAR(8) = CHAR(39) + CHAR(39) + CONVERT(NVARCHAR(8), #endDate, 112) + CHAR(39) + CHAR(39);
Note I change the data types and styles too. The style as yyyy-MM-dd isn't unambiguous in SQL Server, and the data types, as you don't need 2 billion characters for a date.
Note, however, that after executing this you still won't be able to access #Temp1. A temporary table only persists for the scope it was created in, and that scope is the dynamic SQL's. You'll need to use a permanent table inside the dynamic statement, or CREATE a temporary table outside of the dynamic statement to use it outside of it.

SQL select statement to a parameter

I am trying to build a parameter, that contains a comma separated list columns, so I can use this dynamically later on. However i'm having an issue writing my select query, into a parameter that can be used to a separate select.
This is my parameter, to define the comma seperated list;
DECLARE #sqlSrcCols VARCHAR(MAX)
SET #sqlSrcCols = '
SELECT STUFF((SELECT '','' + a.sourceName
FROM #matchedFields a
FOR XML PATH('''')), 1, 1, '''') AS listStr'
This outputs a comma seperated list like so;
abc,cde,dfg,thy
I want to then reference that list, in a query like below:
DECLARE #sqlSelect VARCHAR(MAX) = 'SELECT ' + #sqlSrcCols + 'FROM a.TableName WHERE ExpDat = ''2020-01-01'' '
EXEC(#sqlSelect)
Although I can't use #sqlSrcCols within my query, and I can't use EXEC(#sqlSrcCols).
Any help welcomed.
At a guess, but I think what you actually mean to do is:
DECLARE #sqlSrcCols nvarchar(MAX); --Fixed datatype
SET #sqlSrcCols = STUFF((SELECT N',' + QUOTENAME(mF.sourceName) --Quoting is important
FROM #matchedFields mF --"a" for matchedFields?
FOR XML PATH(N''), TYPE).value('.', 'nvarchar(MAX)'),1,1,''); --used TYPE, in case of any odd characters
DECLARE #sqlSelect nvarchar(MAX);
SET #sqlSelect = N'SELECT ' + #sqlSrcCols + N' FROM a.TableName WHERE ExpDat = ''20200101'';'; --ISO dates are better, they won't fail depending on language/data type
--PRINT #SQL; --You debugging friend
EXEC sp_executesql #SQL; --Don't use EXEC(#SQL), use sp_executesql so you can parametrise when needed.
You weren't setting the value of #sqlSrcCols to be the names of the columns, you were setting it to the SQL statement. Then, when you concatenated the 2 variables, you ended up with a statement that would have looked like this:
SELECT
SELECT STUFF((SELECT ',' + a.sourceName
FROM #matchedFields a
FOR XML PATH('')), 1, 1, '') AS listStrFROM a.TableName WHERE ExpDat = '2020-01-01'
Which, as you can see, doesn't make any sense.
Bonus: I would actually write the above like you see below. The reason being is that then you have formatting in your dynamic SQL too; making it easier to debug. I have also parametrised #ExpDat, incase it needs to be (and this therefore shows you have to parametrise a dynamic statement):
DECLARE #sqlSrcCols nvarchar(MAX), --Fixed datatype
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #sqlSrcCols = STUFF((SELECT N',' + #CRLF +
N' ' + QUOTENAME(mF.sourceName) --Quoting is important
FROM #matchedFields mF --"a" for matchedFields?
FOR XML PATH(N''), TYPE).value('.', 'nvarchar(MAX)'),1,10,N''); --used TYPE, in case of any odd characters
DECLARE #sqlSelect nvarchar(MAX);
SET #sqlSelect = N'SELECT ' + #sqlSrcCols + #CRLF +
N'FROM a.TableName' + #CRLF +
N'WHERE ExpDat = #ExpDat;'; --ISO dates are better, they won't fail depending on language/data type
--PRINT #SQL; --You debugging friend
DECLARE #ExpDat date = '20200101';
EXEC sys.sp_executesql #SQL, N'#ExpDat date', #ExpDat; --Don't use EXEC(#SQL), use sp_executesql so you can parametrise when needed.
If you're interested, you can find some of pointers on Dynamic SQL, what to do and not to do, in my article on SSC.

Using a temporary table on SRSS report

I have a stored procedure which works without a problem at the sql server side. However, when I feed a SRSS report with this stored procedure, I am having an error such as; Invalid object name '##tempTable'.
Here is my stored procedure;
ALTER PROCEDURE [dbo].[Link_SP_Inventory]
-- Add the parameters for the stored procedure here
#StoreId int,
#StartDate date,
#EndDate date
AS
BEGIN
DECLARE #QUERY nvarchar(MAX);
SET #QUERY = N'SELECT * INTO ##tempTable ' +
N'FROM OPENQUERY("172.11.111.11", N''EXEC [DB].dbo.SP_inventory ' + CONVERT(varchar(10),#StoreId) + ',' + '''' + QUOTENAME(CONVERT(varchar,#StartDate,112),'''') + '''' + ',' + '''' + QUOTENAME(CONVERT(varchar,#EndDate,112) + '''','''') + ')';
EXEC sp_executesql #QUERY;
SET NOCOUNT ON;
SELECT * FROM ##tempTable
drop table ##tempTable
END
GO
How can I solve this issue? Thanks.
A Table referenced in a Dynamic statement can only be referenced inside that dynamic statement. Take this simple query:
EXEC sp_executesql N'SELECT 1 AS I INTO #temp;';
SELECT *
FROM #temp;
Notice the statement fails with:
Msg 208, Level 16, State 0, Line 3
Invalid object name '#temp'.
It seems, however, you don't need to temporary table, and this will work fine:
ALTER PROCEDURE [dbo].[Link_SP_Inventory]
-- Add the parameters for the stored procedure here
#StoreId int,
#StartDate date,
#EndDate date
AS
BEGIN
DECLARE #QUERY nvarchar(MAX);
SET #QUERY = N'SELECT * ' +
N'FROM OPENQUERY("172.11.111.11", N''EXEC [DB].dbo.SP_inventory ' + CONVERT(varchar(10),#StoreId) + ',' + '''' + QUOTENAME(CONVERT(varchar(8),#StartDate,112),'''') + '''' + ',' + '''' + QUOTENAME(CONVERT(varchar(8),#EndDate,112) + '''','''') + ')';
EXEC sp_executesql #QUERY;
END;
Try doing this
ALTER PROCEDURE [dbo].[Link_SP_Inventory]
-- Add the parameters for the stored procedure here
#StoreId int,
#StartDate date,
#EndDate date
AS
BEGIN
DECLARE #QUERY nvarchar(MAX);
SET #QUERY = N'SELECT * INTO ##temp_global ' +
N'FROM OPENQUERY("172.11.111.11", N''EXEC [DB].dbo.SP_inventory ' + CONVERT(varchar(10),#StoreId) + ',' + '''' + QUOTENAME(CONVERT(varchar,#StartDate,112),'''') '''' + ',' + '''' + QUOTENAME(CONVERT(varchar,#EndDate,112) + '''','''') + ')';
EXECUTE (#QUERY)
SELECT * FROM ##temp_global
DROP TABLE ##temp_global
END
I think I had this once and I had to create the temp table initially with the column names specified and then insert into it, so that your dataset would be able to pick up the column names. So something like this may work:
ALTER PROCEDURE [dbo].[Link_SP_Inventory]
-- Add the parameters for the stored procedure here
#StoreId int,
#StartDate date,
#EndDate date
AS
BEGIN
DECLARE #QUERY nvarchar(MAX);
CREATE TABLE ##tempTable ([ColumnOne] VARCHAR(10), [ColumnTwo] DATETIME) --Add required columns here
SET #QUERY = N'SELECT * FROM OPENQUERY("172.11.111.11", N''EXEC [DB].dbo.SP_inventory ' + CONVERT(varchar(10),#StoreId) + ',' + '''' + QUOTENAME(CONVERT(varchar,#StartDate,112),'''') + '''' + ',' + '''' + QUOTENAME(CONVERT(varchar,#EndDate,112) + '''','''') + ')';
INSERT INTO ##tempTable ([ColumnOne],[ColumnTwo])
EXEC (#QUERY);
SET NOCOUNT ON;
SELECT * FROM ##tempTable
DROP TABLE ##tempTable
END
GO

SQL Parameterised column names

I am trying to create a parameterised query for retrieving data back from a table
Essentially I have a table structure of
ID
nvarchar1
ntext
datetime1
datetime2
and I am trying to do a query like so that it selects all the data where the current date is greater than datetime1 and less than datetime2
SELECT
ID, nvarchar1,
ntext,
datetime1,
datetime2
FROM
TABLEName
WHERE
datetime1 >= #CurrentDate
AND datetime2 <= #CurrentDate
I want to make the columns parameters such as
#TableName, #CurrentDate, #StartDate, #EndDate
DECLARE #TableName NVARCHAR(100);
SET #TableName = '[Surveys].[dbo].[Table]'
DECLARE #CurrentDate DateTime;
SET #CurrentDate = GETDATE();
DECLARE #StartDate NVARCHAR(100);
SET #StartDate = 'datetime1'
DECLARE #EndDate NVARCHAR(100);
SET #EndDate = 'datetime2'
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #TableName + 'WHERE' + #EndDate + '>=' + #CurrentDate + 'AND' + #StartDatedatetime1 + '<=' + #CurrentDate
EXEC(#sql)
The data is going to be coming from a SP data source so I have no control of the column names etc. and when I create the SP Lists they automatically assign to a table column of that type so this is why I need to columns to be parameters.
Using the above code which I thought should work returns
Msg 241, Level 16, State 1, Line 14
Conversion failed when converting date and/or time from character string.
What am I doing wrong?
Try the below. As #GordonLinoff stated, you where missing the single quotes (') from around the #CurrentDate variable. Also, you where passing a DATETIME parameter in to the #sql variable which is an NVARCHAR data type. These are not implicitly converted, so the #CurrentDate variable needs to be converted to a NVARCHAR first:
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #TableName
+ ' WHERE ' + #EndDate + ' >= ''' + CONVERT(NVARCHAR(50), #CurrentDate,120)
+ ''' AND ' + #StartDate + ' <= ''' + CONVERT(NVARCHAR(50), #CurrentDate,120) + ''''
EXEC(#sql)
You are pretty close. I would construct the SQL for the table and then use parameters for the current date: This would be something like this:
SET #sql = '
SELECT *
FROM #TableName
WHERE datetime2 >= #CurrentDate AND datetime1 <= #CurrentDate';
SET #sql = REPLACE(#sql, '#TableName', #TableName);
exec sp_executesql #sql, N'#CurrentDate date', #CurrentDate = #CurrentDate;
Incidentally, the problem with your query is the lack of single quotes around the date constants.
EDIT:
You cannot substitute column or table names using parameters. I would write the code as:
SET #sql = '
SELECT *
FROM #TableName
WHERE #datetime2 >= #CurrentDate AND #datetime1 <= #CurrentDate';
SET #sql = REPLACE(#sql, '#TableName', #TableName);
SET #sql = REPLACE(#sql, '#datetime1', #DateTime1);
SET #sql = REPLACE(#sql, '#datetime2', '#DateTime2);
. . .
I use REPLACE() for this type of operation because the code is easier to understand and to maintain.

exec sp_executesql error 'Incorrect syntax near 1' when using datetime parameter

I have a SSRSreport which sends the following text to the database :
EXEC ( 'DECLARE #TeamIds as TeamIdTableType ' + #Teams +
' EXEC rpt.DWTypeOfSicknessByCategoryReport #TeamIds , ' +
#DateFrom + ', ' + #DateTo + ', ' + #InputRankGroups + ', ' +
#SubCategories )
When I view this in profiler it interprets this as :
exec sp_executesql N'EXEC ( ''DECLARE #TeamIds as TeamIdTableType '' + #Teams +
'' EXEC rpt.DWTypeOfSicknessByCategoryAndEmployeeDetailsReport #TeamIds, '' +
#DateFrom + '', '' + #DateTo + '', '' + #InputRankGroups + '', '' +
#SubCategories )',
N'#Teams nvarchar(34),#DateFrom datetime,#DateTo datetime,
#InputRankGroups varchar(1),#SubCategories bit',
#Teams=N'INSERT INTO #TeamIds VALUES (5);',
#DateFrom='2010-02-01 00:00:00',#DateTo='2010-04-30 00:00:00',
#InputRankGroups=N'1',#SubCategories=1
When this sql runs it errors on the dates.
I have tried changing the format of the date but it does not help. If I remove the dates it works fine.
I think I know what's going on here. If the SQL that is executed is
EXEC ( 'DECLARE #TeamIds as TeamIdTableType ' + #Teams
+ ' EXEC rpt.DWTypeOfSicknessByCategoryReport #TeamIds , '
+ #DateFrom + ', '
+ #DateTo + ', '
+ #InputRankGroups + ', '
+ #SubCategories )
... SQL Server will do the string concatenation first and then call EXEC on the concatenated string. With the values of the parameters given, this means the concatenated string will be something like the following (with whitespace adjusted for readability):
DECLARE #TeamIds as TeamIdTableType
INSERT INTO #TeamIds VALUES (5);
EXEC rpt.DWTypeOfSicknessByCategoryReport #TeamIds ,
2010-02-01 00:00:00, 2010-04-30 00:00:00, 1, 1
The problem is that EXEC is not a parameterized query, so it has to interpret the datetime parameters as raw strings, but as raw unquoted strings they are not proper syntax. You can fix this one of two ways.
First, the easy way: quote the datetime strings.
EXEC ( 'DECLARE #TeamIds as TeamIdTableType ' + #Teams
+ ' EXEC rpt.DWTypeOfSicknessByCategoryReport #TeamIds , '
+ '''' + #DateFrom + ''', '
+ '''' + #DateTo + ''', '
+ #InputRankGroups + ', '
+ #SubCategories )
This gives you something with valid syntax:
DECLARE #TeamIds as TeamIdTableType
INSERT INTO #TeamIds VALUES (5);
EXEC rpt.DWTypeOfSicknessByCategoryReport #TeamIds ,
'2010-02-01 00:00:00', '2010-04-30 00:00:00', 1, 1
Second, the almost-as-easy but more correct way: use a parameterized query.
DECLARE #sql varchar(max)
SET #sql = 'DECLARE #TeamIds AS TeamIdTableType ' + #Teams
+ ' EXEC rpt.DWTypeOfSicknessByCategoryReport #TeamIds , #DateFrom, #DateTo, #InputRankGroups, #SubCategories'
EXEC sp_executesql #sql, N'#DateFrom datetime,#DateTo datetime,#InputRankGroups varchar(1),#SubCategories bit',
#DateFrom, #DateTo, #InputRankGroups, #SubCategories
This way you don't have to worry about how to escape particular value types, although it is slightly more verbose. Note that you can't completely parameterize it due to passing raw SQL as a parameter in #Teams. This may not be something you can control -- I'm not that familiar with SSRS.
Thanks for sharing your solution. I tried to implement the 2nd one but I was getting an error invoking the stored procedure. I had to tweak the syntax a little bit and this worked for me:
DECLARE #vSQL nvarchar(max) = N'
DECLARE #customerIdList Report.IntegerListTableType;
'+#customerIdInserts+';
EXEC rpt_CustomerTransactionSummary
#startDate=#startDate,
#endDate=#endDate,
#accountType=#accountType,
#customerIds = #customerIdList';
exec sp_executesql
#vSQL,
N'#startDate datetime, #endDate datetime, #accountType int',
#startDate,
#endDate',
#accountType;
This syntax allows for NULL values too (for example, #accountType=NULL)

Resources