How can I get data back from a dynamic call to OPENROWSET? - sql-server

One strange attribute of T-SQL's OPENROWSET() function is that it cannot accept parameters within the 'query' which it executes remotely. In order to get around this I guess you have to create a long string containing the OPENROWSET calls and the parametrized query.
Given this limitation, I'm trying to get this piece of code to work:
Declare #DataId int
Declare #RecordType varchar(50)
Declare #Filter varchar(50)
-- ...
SELECT TOP 1
#RecordType = recordType,
#DataId = DataId
FROM OPENROWSET('SQLNCLI',
'Server=MyServer;Trusted_Connection=yes;',
'SELECT recordType, DataId FROM MyDb..data_lookup
WHERE Filter = ''' + #Filter+'''')
This throws an error
Incorrect syntax near '+'
Right now, which makes sense given the restriction on OPENROWSET. But if I convert this to SQL string, won't I lose the ability to set #RecordType and #DataId from the results of the query?
Is there any syntactic sugar I can sprinkle on this to get around the restriction and make this work the way I want it to?

Here's some examples of building a string dynamically:
http://support.microsoft.com/kb/314520
You could insert into a table variable first, and then pull your values from there.
DECLARE #t TABLE (DataID int, RecordType varchar(50))
INSERT INTO #t
exec sp_executeSQL N'your OPENROWSERT query'
SELECT TOP 1 #DataID = DataID, #RecordType = RecordType
FROM #t

Thanks to #Stuart, here is the code that I finally went with:
Declare #DataId int
Declare #RecordType varchar(50)
Declare #Filter varchar(50)
Declare #query nvarchar(4000)
-- ...
select #query = N'select top 1 recordType, DataId
from openrowset(''SQLNCLI'',
''Server=MyServer;Trusted_Connection=yes;'',
''SELECT recordType, DataId from MyDb..data_lookup
where Filter = ''''' + #Filter+''''''')'
declare #t TABLE (recordType varchar(2), DataId int)
insert into #t
exec sp_executeSQL #Query
select top 1 #RecordType = recordType, #DataId = DataId
from #t

Related

Passing Variable Into OpenQUERY SQL Server 2016

EDIT: SQL Server Version
I'm trying to pass this variable into my open query using this guide from Microsoft: Link
I'm running into this error message "Statement(s) could not be prepared." Which I believe means something is wrong with the OpenQuery. I'm just not sure what is wrong.
Here's the code:
DECLARE #ticketid INT, #QLFD VARCHAR(8000)
SELECT #ticketid = '296272348'
SELECT #QLFD = 'SELECT
*
FROM
OPENQUERY(
[Server_name],''
SELECT
ticket_id
, QLFD_SPD_AMT
FROM [database].[dbo].[table]
WHERE ticket_id = #ticketid
'')'
EXEC (#QLFD)
Could you help me identify the error? I prefer to do it passing the whole query as one.
Thanks!
Edit:
After looking at suggestions made by #Larnu. I have adjusted my code to:
DECLARE #ticketid INT--, #QLFD NVARCHAR(Max)
SELECT #ticketid = '296272348'
DECLARE #QLFD NVARCHAR(Max) = 'SELECT
*
FROM
OPENQUERY(
[Server_name],''
SELECT
ticket_id
, QLFD_SPD_AMT
FROM [database].[dbo].[table]
WHERE ticket_id = QUOTENAME(#ticketid, '''''''')
'')';
EXEC (#QLFD);
As I mentioned, you can't parametrise a query with OPENQUERY you have safely inject the values.
Normally that would be with QUOTENAME or REPLACE, but you don't actually need to do that here, due to the value being a numerical data type, so you can just concatenate it in:
DECLARE #ticketid int = 296272348; --Don't wrap numerical datatypes with quotes.
DECLARE #SQL nvarchar(MAX),
#OpenQuery nvarchar(4000);
SET #OpenQuery = CONCAT(N'SELECT QLFD_SPD_AMT
FROM [database].[dbo].[table]
WHERE ticket_id = ',#ticketid,N';'); --As it's an int we dont need to quote
SET #SQL = CONCAT(N'SELECT #ticketid AS ticket_id, QLFD_SPD_AMT
FROM OPENQUERY([servername],N''',REPLACE(#OpenQuery,'''',''''''),N''');';
EXEC sys.sp_executesql #SQL, N'#ticketid int', #ticketid;

Cannot pass column name as parameter to sp_executesql

I'm having trouble executing the below piece of code, it's giving me an error as below:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '#ST'.
I can try implementing the login using dynamic SQL, but wanted to try the sp_executesql method. Please let me know if I'm having some syntax error or I'm not supposed to pass table names as parameters?
DECLARE #SQL NVARCHAR(4000)= '';
SET #SQL = N'--INSERT INTO #missingkeys ( SOURCE_KEY,[ROWCOUNT] )
SELECT S.[SOURCE_KEY], COUNT(1) AS [ROWCOUNT] FROM (SELECT DISTINCT #SK AS [SOURCE_KEY]
FROM [PDA].#ST ) AS S
LEFT JOIN [PDA].#MT AS T
ON T.[SOURCE_KEY] = S.[SOURCE_KEY]
GROUP BY S.[SOURCE_KEY]';
DECLARE #SOURCETABLE NVARCHAR(255)= 'FACT';
DECLARE #SOURCE_KEY NVARCHAR(255)= 'KEY', #MAP_TABLE NVARCHAR(255)= 'DimMap';
EXEC sp_executesql
#SQL,
N'#SK nvarchar(255), #ST nVARCHAR(255), #MT nVARCHAR(255)',
#SK = #SOURCE_KEY,
#ST = #SOURCETABLE,
#MT = #MAP_TABLE;
You can't have columns as parameters, same for any object name (table, stored procedure, ...).
You will have to make the statement dynamic, i.e. format the column name in the SQL string:
SET #SQL =
N'SELECT '+
'S.[SOURCE_KEY],'+
'COUNT(1) AS [ROWCOUNT] '+
'FROM ('+
'SELECT DISTINCT '+
QUOTENAME(#SK)+' AS [SOURCE_KEY] '+
'...'; -- the rest of your statement
PS: Use QUOTENAME to escape object names to avoid SQL Injection.
You can pass tables if you write the data to an identical user-defined table type. The parameter must be READONLY:
CREATE TYPE [dbo].[t] AS TABLE([a] [int] NOT NULL PRIMARY KEY CLUSTERED)
create table #t (a int)
insert into #t values (1), (2), (3)
exec sp_executesql N'select * from #t'
declare #t t
insert into #t select a from #t
exec sp_executesql N'Select * from #p1', N'#p1 t readonly', #t

SQL Server declare and execute query for temp table

I have a temp table like
DECLARE #t TABLE (a BIGINT)
I want to do this
SELECT * FROM #t
but I need to do it by EXEC query, and this doesn't work
DECLARE #query AS NVARCHAR(100) = 'SELECT * FROM #t'
EXEC(#query)
how can I create a custom query to select the temp table?
Thanks.
This is because EXEC statement will execute the Statements in new Session. And the Table variables scope is fixed to the batch of statement.
Since you declared the Table Variable out side of the Session , you can't access the table variable in EXEC statement.
So you need to DECLARE ,INSERT, UPDATE, SELECT table variables in the Dynamic Code itself.
DECLARE #query NVARCHAR(MAX)='';
SELECT #query ='
DECLARE #t TABLE
(
a BIGINT
)'
SELECT #query += 'SELECT * FROM #t'
EXEC(#query)
Solution 2:
The another Solution is to create Global Temporary Table which we can create using ##. And Global Temporary tables scope is limited to Database all connections.
CREATE TABLE ##TABLE1
(
a BIGINT
)
DECLARE #query NVARCHAR(MAX)='';
SELECT #query += 'SELECT * FROM ##TABLE1'
EXEC(#query)
But be aware if another user execute the same Query, there might be conflict with the same name creation.
Try hash table as below:
DECLARE #tblTest AS Table
(
Name VARCHAR(50)
)
insert into #tblTest values('Sandip')
insert into #tblTest values('AAA')
IF OBJECT_ID('tempdb..#tblTest') IS NOT NULL
DROP TABLE #tblTest
SELECT * INTO #tblTest FROM #tblTest
DECLARE #query AS NVARCHAR(100) = 'SELECT * FROM #tblTest'
EXEC (#query)
Output:
CREATE TYPE MyTable AS TABLE
(
a BIGINT
);
GO
DECLARE #T AS MyTable;
EXEC sp_executesql
N'SELECT * FROM #T',
N'#T MyTable READONLY',
#T=#T
please try this

Variable table name in select statement

I have some tables for storing different file information, like thumbs, images, datasheets, ...
I'm writing a stored procedure to retrieve filename of a specific ID. something like:
CREATE PROCEDURE get_file_name(
#id int,
#table nvarchar(50)
)as
if #table='images'
select [filename] from images
where id = #id
if #table='icons'
select [filename] from icons
where id = #id
....
How can I rewrite this procedure using case when statement or should I just use table name as variable?
You can't use case .. when to switch between a table in the FROM clause (like you can in a conditional ORDER BY). i.e. so the following:
select * from
case when 1=1
then t1
else t2
end;
won't work.
So you'll need to use dynamic SQL. It's best to parameterize the query as far as possible, for example the #id value can be parameterized:
-- Validate #table is E ['images', 'icons', ... other valid names here]
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'select [filename] from **TABLE** where id = #id';
SET #sql = REPLACE(#sql, '**TABLE**', #table);
sp_executesql #sql, N'#id INT', #id = #id;
As with all dynamic Sql, note that unparameterized values which are substituted into the query (like #table), make the query vulnerable to Sql Injection attacks. As a result, I would suggest that you ensure that #table comes from a trusted source, or better still, the value of #table is compared to a white list of permissable tables prior to execution of the query.
Just build SQL string in another variable and EXECUTE it
DECLARE #sql AS NCHAR(500)
SET #sql=
'SELECT [filename] '+
' FROM '+#table+
' WHERE id = #id'
EXECUTE(#sql)
CREATE PROCEDURE get_file_name(
#id int,
#table nvarchar(50)
)as
DECLARE #SQL nvarchar(max);
SET #SQL = 'select [filename] from ' + #table + ' where id = ' + #id
EXECUTE (#SQL)

How can we use EXEC sp_executesql for two dynamic SQL statements?

tables:
create table TabA
(ID int, Name varchar(20))
insert into TabA
select 1,'ABC' union
select 2,'DEF' union
select 3,'GHD'
create table TabB
(ID int, Name varchar(20))
insert into TabA
select 1,'XYZ' union
select 2,'STF' union
select 3,'LDZ'
create table status
(Result1 int,Result2 int )
Create table query(query1 varchar(1000),query2 varchar(1000))
Insert into query(query1,query2)
select '''select COUNT(*) from TabA''','''select COUNT(* )from TabB'''
select * from query
procedure:
create Procedure [dbo].spStatus
AS
BEGIN
SET NOCOUNT ON;
Declare #sqlString1 nvarchar(1000)
,#sqlString2 nvarchar(1000)
,#col_value1 varchar(256)
,#col_value2 varchar(256)
select #sqlString1 = query1
, #sqlString2 =query2
from Query
EXEC sp_executesql
#query=#sqlString1, --sql string is your full select statement
#params = N'#col_Value1 varchar(256) OUTPUT',
#col_Value1 = #col_Value1 OUTPUT
print(#sqlString1)
-- #sqlString2, --sql string is your full select statement
--#params = N'#col_Value2 varchar(256) OUTPUT',
-- #col_Value2 = #col_Value2 OUTPUT
Insert Into dbo.Status(Result1,Result2 )
Values(#col_Value1,#col_Value2)
End
It works if we use #query=#sqlString1 only but I want both statement #query=#sqlString1,#query=#sqlString2 should execute together.
Please help how can we use both statement to execute?
Thanks in Advance
Did you mean:
SET #sqlString1 = #sqlString1 + ';' + #sqlString2;
EXEC sp_executesql #query = #sqlString1 --...
Concat the two queries together with a + (#query=#sqlString1 + '; ' + #sqlString2)
Then use two variables to capture the two counts into output variables
OR
Insert into query(query1,query2)
EXEC sp_executesql 'SELECT ( select COUNT(*) from TabA ) AS query1, ( select COUNT(*)from TabB ) AS query2'
... but really and truly dynamic SQL isn't needed for that at all.
try this:
--add this
DECLARE #SQL nvarchar(max)
SET #SQL=ISNULL(#sqlString1,'')+';'+ISNULL(#sqlString2,'')
--change this
EXEC sp_executesql #query=#SQL
,#params = N'#col_Value1 varchar(256) OUTPUT'
,#col_Value1 = #col_Value1 OUTPUT

Resources