What am I missing to execute stored procedure in OpenQuery - sql-server

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.

Related

Dynamic OPENQUERY with DATETIME criteria

Can someone please explain to me what is wrong with the below expression. I believe that's I'm converting my date correctly using CONVERT 126 and that my quotes are escaped correctly using char 39 but I could be wrong.
T-SQL:
DECLARE #end2 DATETIME2 = GETDATE();
DECLARE #test2 nvarchar(200) = N'SELECT * FROM OPENQUERY(x, '
+ char(39) + 'SELECT OBJID FROM SALE WHERE MODIFIED >= '
+ CHAR(39) + CONVERT(nvarchar(24),#end2,126)
+ char(39) + char(39) + ')';
PRINT #test2;
EXEC (#test2);
Print output:
select * from openquery(x, 'SELECT OBJID FROM SALE
WHERE MODIFIED >= '2023-01-19T11:55:21.1233'')
Error:
Msg 102, Level 15, State 1
Incorrect syntax near '2023'.
Tried different formats, casting, etc. I can't use EXEC x..sys.sp_executesql because x is Firebird, not another SQL Server.
You can escape the ' character with another one, i.e. ''. But you need to double escape it, i.e. your final string needs to have double single quotes in to be escaped in your dynamic SQL, which means a lot of escaping, i.e.
DECLARE #end2 DATETIME2
set #end2 = getdate()
declare #test2 nvarchar(200)
set #test2 = 'select * from openquery(x, ''SELECT OBJID FROM SALE WHERE MODIFIED >= '''''+convert(nvarchar(24),#end2,126)+''''''')'
print #test2
exec (#test2)
Which results in:
select *
from openquery(x, 'SELECT OBJID FROM SALE WHERE MODIFIED >= ''2023-01-19T18:06:22.6033''')

Passing parameters to a OPENQUERY

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}];

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.

SQL Server EXEC backup table with date dynamically

I am looking to backup a table and auto add the date to the end of the table name.
Here is what I have
declare #table char(36)= 'template_fields'
EXEC('select * into '+#table+'_'+'convert(date, getdate()) from '+#table)
And I want the end result to look something like
template_fields_09-09-2015
What am I missing here?
Just print what you do:
DECLARE #table NVARCHAR(MAX) = 'tab';
DECLARE #sql NVARCHAR(MAX) = 'select * into '+#table+'_'
+'convert(date, getdate()) from '+#table;
SELECT #sql;
you will get: select * into tab_convert(date, getdate()) from tab
You need to pass date with table name like:
SqlFiddleDemo
DECLARE #table NVARCHAR(MAX) = 'tab';
DECLARE #new_table NVARCHAR(MAX) = #table + '_' +
CONVERT(NVARCHAR(100), GETDATE(),105);
DECLARE #sql NVARCHAR(MAX) = 'select * into ' + #new_table + ' from '+ #table;
SELECT #sql;
/* Result select * into tab_09-09-2015 from tab */
-- EXEC(#sql);
First of all, do not use EXEC to run dynamic queries, use sp_executesql instead.
Second: When you want to build a SQL query with variable object names, use QUOTENAME().
DECLARE #table sys.sysname = 'mytable'
DECLARE #backup sys.sysname = #table + '_' + CONVERT(date, GETDATE());
DECLARE #sql NVARCHAR(MAX) = 'SELECT * INTO '
+ QUOTENAME(#backup) + ' '
+ FROM + ' '
+ QUOTENAME(#table);
EXEC sp_executesql
#stmt = #sql
Please note that, the sys.sysname is a built in data type (essentially an alias for NVARCHAR(128) NOT NULL) and SQL Server uses it internally for object names.
Note: I have no SQL Server instance accessible right now, so the above query can contain typos.
I ended up resolving this myself. While some nice responses were added I wrote my initial query more simply to achieve this. Thank you all for the help.
--Declare and initiate the table
declare #table varchar(36)= 'template_fields'
--Declare and initiate the date (YYYYMMDD) without dashes
declare #date varchar(10) = convert(int, convert(varchar(10), getdate(), 112))
--Execute the query with the variables resulting in a new table titled 'template_fields_20150909'
EXEC('select * into '+#table+'_'+#date+' from '+#table)

SQL Server: executing dynamic/inline sql table name within EXEC and passing geometry as geometry parameter

I'm trying to execute an inline SQL statement within a stored procedure. I'm working with SQL Server 2008.
The problem is that I can't execute the first inline statement (with WHERE clause). It crashes because the string within EXEC(...) is dynamically created and all concatenated variables must be of type varchar.
Error that appears when calling procedure:
An expression of non-boolean type specified in a context where a
condition is expected, near 'ORDER'.
The procedure looks like:
CREATE PROCEDURE loadMyRows
#table_name nvarchar(50),
#bounding_box varchar(8000)
AS
BEGIN
-- *********************************** COMMENT *********************************
-- ** This two code lines are correct and will return true (1) or false (0), **
-- ** but they doesn't work within inline EXEC(...) **
--DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
--select TOP(5) wkt.STWithin(#bb) AS 'bool'
-- *********************************** COMMENT *********************************
IF #bounding_box <> ''
BEGIN
DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
EXEC(
'SELECT TOP (' + #row_limit + ') * ' +
'FROM ' + #real_table_name + ' ' +
'WHERE wkt.STWithin('+#bb+') ' + -- <-- doesn't work :-(
-- 'WHERE wkt.STWithin(geometry::STGeomFromText('''+#bounding_box+''', 4326)) ' +
-- ^^ doesn't work, too :-(
'ORDER BY id ASC '
);
END
ELSE
BEGIN
EXEC(
'SELECT TOP (' + #row_limit + ') * ' +
'FROM ' + #real_table_name + ' ' +
'ORDER BY id ASC'
);
END
END
I've found a working solution for this problem. The way the MSDN showed me was http://msdn.microsoft.com/en-US/library/ms175170.aspx. There's written:
[...] the string is executed as its own self-contained batch.
That let me know, if I want to execute a dynamic statement with a table variable as string, it's the same as I would execute the query without the EXECUTE command, like:
SELECT TOP(#row_limit) *
FROM #real_table_name
WHERE ...
ORDER BY id ASC;
And this would probably not work for the table name.
So, if I write instead:
DECLARE #sql_statement nvarchar(MAX) = 'SELECT TOP(#limit) *
FROM ' + #real_table_name + '
ORDER BY id ASC';
-- declaration of parameters for above sql
DECLARE #sql_param_def nvarchar(MAX) = '#limit int';
EXECUTE sp_executesql #sql_statement, #sql_param_def, #limit = #row_limit;
Then, this would work. This is because I define the #sql_statement simply as a concatenated string which will just resolve the dynamic table name at runtime to a string with the name of the real existing table. The #limit parameter is untouched and is still a parameter.
If we then execute the batch we only must pass a value for the #limit parameter and it works!
For the geometry parameter it works in the same way:
DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
SET #sql_statement = 'SELECT TOP(#limit) *
FROM ' + #real_table_name + '
WHERE wkt.STWithin(#geobb) = 1
ORDER BY id ASC';
-- NOTE: This ' = 1' must be set to avoid my above described error (STWithin doesn't return a BOOLEAN!!)
-- declaration of parameters for above sql
SET #sql_param_def = '#limit int, #geobb geometry';
EXECUTE sp_executesql #sql_statement, #sql_param_def, #limit = #row_limit, #geobb = #bb;
Hope this was clear ;-)
create proc usp_insert_Proc_Into_temp
#tempTable nvarchar(10) output
as
begin
set #tempTable = '##temp'
declare #query nvarchar(200)
--Select statement
set #query = 'select 1 as A,2 as B, 3 as C into'+ ' '+#tempTable+''
exec(#query)
end
go
declare #tempTable nvarchar(10)
exec usp_insert_Proc_Into_temp #tempTable output
exec('select * from' + ' '+ #tempTable+'')
exec ('drop table'+ ' '+#tempTable+'')

Resources