Dynamic OPENQUERY with DATETIME criteria - sql-server

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''')

Related

Conversion failed when converting the varchar value '43302001-8' to data type int

When I try to execute the following:
DECLARE #sqlText nvarchar(1000);
SET #sqlText = N'SELECT TOP ' + CAST((:intValue) AS VARCHAR(20)) + 't_stamp, PART_NUM, ' + :Column + ' FROM dbo.press_7_recipes_log WHERE PART_NUM = ' + CAST(:text AS VARCHAR(20))
Exec (#sqlText)
I am getting the following
error:com.microsoft.sqlserver.jdbc.SQLServerException: Conversion failed when converting the varchar value '43302001-8' to data type int.
Any help would be greatly appreciated, not sure what else is required here.
:intValue is of type int4
:text is of type string
:Column is of type string (This is pulling a specified column from the database and why I think this needed to be a dynamic query)
Tried multiple attempts at googling the issue and changing the command with the same outcome. If I change the PART_NUM in the where to a column that is of type int the code works fine, any string related column does not.
The problem is that after your preparation the query becomes:
SELECT TOP 666 t_stamp, PART_NUM, ANOTHER_COLUMN FROM dbo.press_7_recipes_log WHERE PART_NUM = 43302001-8
And since 43302001-8 is an INTEGER=43301993, SQL Server converts PART_NUM column to INT, which doesn't work since it probably contains non-integers.
You need to change your dynamic query to this me thinks:
DECLARE #sqlText nvarchar(1000);
SET #sqlText = N'SELECT TOP ' + CAST((:intValue) AS VARCHAR(20)) + 't_stamp, PART_NUM, ' + :Column + ' FROM dbo.press_7_recipes_log WHERE PART_NUM = ''' + REPLACE(CAST(:text AS VARCHAR(20)), '''', '''''') + ''''
Exec (#sqlText)
This will change WHERE to: PART_NUM = '43302001-8'
But as others noticed, you have a lot of possibilities for SQL Injections here. So i'd probably get rid of this code and rewrite it to avoid dynamic SQL

Passing variables in OPENQUERY

I have no doubt I'm missing something minute, but nonetheless, I'm not sure what it is. Here's the query:
DECLARE #START DATE, #END DATE, #MySQL VARCHAR(MAX)
SELECT #START = '12/1/2020'
SELECT #END = '12/10/2020'
SET #MySQL =
'SELECT * FROM OPENQUERY ([SERVERNAME], ''SELECT * FROM TABLE (NOLOCK) WHERE InitiatedDate BETWEEN ' + #Start + ' AND ' + #End + ')'
EXEC (#MySQL)
I get this error:
The data types varchar and date are incompatible in the add operator
I tested this concatenation method with a similar query and it worked, no problem:
SET #MySQL =
'SELECT * FROM OPENQUERY ([SERVER], ''SELECT top ' + #X + ' * FROM TABLE'')'
EXEC (#MySQL)
Your second example is probably working because #X was a VARCHAR data type.
In your main example, #START and #END are defined as DATE data types, so logically you cannot append them to a VARCHAR data type in the same way that adding 6 and 'six' doesn't result in either of 12 or '6six'.
You need to make use of the CONVERT function to change your DATE back to the string representation of a DATE. Be careful to use the format option to get the string in a format that can then be automatically converted back to a DATE when the query runs.
'SELECT * FROM OPENQUERY ([SERVERNAME], ''SELECT * FROM TABLE (NOLOCK) WHERE InitiatedDate BETWEEN ''' + CONVERT(varchar(10), #Start, 101) + ''' AND ''' + CONVERT(varchar(10), #End, 101) + ''')'

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.

Msg 102, Level 15, State 1, Line 3 Incorrect syntax near ' ' with sp_executesql

I am dynamically building a SQL statement based on operations from a couple of different tables. The salient part of the SQL is below.
DECLARE #SQL NVARCHAR(MAX) = NULL
...
SELECT #sql = 'TRIM(CAST(' + STRING_AGG(EXPORT_COL, ' AS VARCHAR)) + '','' + TRIM(CAST(') FROM #TEMP_TABLE
SET #sql = 'SELECT''(''+'+#sql+' AS VARCHAR))+'')'''+'FROM '+'[mydatabase].[dbo].['+#TABLENAME+']'
SET #sql = REPLACE(#sql,'''','''''')
When I call the code using sp_executesql
EXEC sp_executesql #sql
I get this error
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ''
If I query #sql or print the value to the messages window in SSMS I get:
SELECT''(''+TRIM(CAST(COL1 AS VARCHAR)) + '','' + TRIM(CAST(COL2 AS VARCHAR))+'')''FROM [mydatabase].[dbo].[DATA_TABLE]
which is the output I would expect.
Copying the text and calling sp_executesql using a quoted version of the output string, the query succeeds with no error.
EXEC sp_executesql N'SELECT''(''+TRIM(CAST(COL1 AS VARCHAR)) + '','' + TRIM(CAST(COL2 AS VARCHAR))+'')''FROM [mydatabase].[dbo].[DATA_TABLE]'
I have already checked for the presence of non-printable characters as indicated in this post
I have also implemented a function that "should" strip out any non printable characters per this post. Yet the problem persists.
SQL Server 2017 Express (v14.0.1000.169) on Windows Server 2019 standard.
You need to be really careful about when and what parts need single quotes vs which parts need doubled quotes.
If you are writing the string to assign it to a variable, it needs the doubled quotes. If, however, the string already has the quote inside, it doesn't need to be doubled again.
Here's a simplified example showing the issues/approach
CREATE TABLE #Test (TestVal varchar(100));
INSERT INTO #Test (TestVal) VALUES ('abcde');
Now, when running the process with doubled quotes (similar to yours), here are the results
DECLARE #SQL2 nvarchar(max) = 'SELECT ''''('''' + TestVal + '''')'''' FROM #Test;'
PRINT #SQL2;
/* -- Result
SELECT ''('' + TestVal + '')'' FROM #Test;
*/
EXEC sp_executesql #SQL2;
/* -- Result
Msg 102, Level 15, State 1, Line 12
Incorrect syntax near ''.
*/
EXEC sp_executesql N'SELECT ''('' + TestVal + '')'' FROM #Test;';
/* -- Result
(abcde)
*/
Note that in the bottom command, the doubled quotes were needed so that the string would contain single quotes - and therefore works. However, when already in the string, it made the command fail.
Now, if we make the variable just have single quotes, it works
DECLARE #SQL3 nvarchar(max) = 'SELECT ''('' + TestVal + '')'' FROM #Test;'
PRINT #SQL3;
/* -- Result
SELECT '(' + TestVal + ')' FROM #Test;
*/
EXEC sp_executesql #SQL3;
/* -- Result
(abcde)
*/

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