Getting OUTPUT from Nested Dynamic SQL with a Remote Server - sql

I am trying to execute some logic on a remote server using dynamic SQL, but when I set the dynamic SQL and try to call it nothing happens.. If I call the execution without making the execution dynamic it works without issue.
Not sure if what I am trying to do is allowed when passing OUTPUT values but figured I would see if anyone has any thoughts
DECLARE #sqlserver NVARCHAR(MAX) = ''
DECLARE #DBName NVARCHAR(MAX) = ''
DECLARE #parms NVARCHAR(MAX) = N'#output INT OUT', #output INT, #sql NVARCHAR(MAX) = '
;WITH DuplicateStatusCodes AS (
SELECT CodeTypeID FROM EDDSDBO.Code WHERE Name = ''Master'' OR Name = ''Unique'' OR Name = ''Duplicate''
)
SELECT ROW_NUMBER() OVER (ORDER BY CodeTypeID) RN, CodeTypeID INTO #temp
FROM DuplicateStatusCodes
GROUP BY CodeTypeID
HAVING COUNT(CodeTypeID) = 3
DECLARE #count INT = (SELECT COUNT(1) FROM #temp)
DECLARE #ctr INT = 1;
IF #count > 0
BEGIN
WHILE #ctr <= #count BEGIN
DECLARE #parms NVARCHAR(MAX) = N''#inneroutput INT OUT'';
DECLARE #inneroutput INT
DECLARE #codeartifact INT = (SELECT CodeTypeID FROM #temp WHERE RN <= 1 * #ctr and RN > 1 * (#ctr - 1))
DECLARE #duplicatestatuscheck NVARCHAR(MAX) = ''SELECT #inneroutput = COUNT(1) FROM eddsdbo.codeartifact_''+CAST(#codeartifact AS VARCHAR)+''''
EXEC sys.sp_executesql #duplicatestatuscheck, #parms, #inneroutput OUT
SELECT #output = #inneroutput
IF #inneroutput > 0
BREAK;
SET #ctr = #ctr + 1;
END
END
ELSE
BEGIN
SELECT #output = ''0''
END
'
DECLARE #linksql NVARCHAR(MAX) = '
DECLARE #sql NVARCHAR(MAX)
DECLARE #parms NVARCHAR(MAX) = N''#output INT OUT''
EXEC '+QUOTENAME(#sqlserver)+'.'+QUOTENAME(#DBName)+'.sys.sp_executesql #sql, #parms, #output = #output OUT'
--This does not work
EXEC sp_executesql #linksql, #parms, #output = #output
PRINT #output
--This works fine
EXEC [RemoteServerName].[DatabaseName].sys.sp_executesql #sql, #parms, #output = #output OUT ```
Appreciate any help

you would need to use OPENROWSET. Something like this:
DECLARE #ServerName VARCHAR(255) = '...'
,#DatabaseName VARCHAR(255) = '...'
,#Pwd VARCHAR(255) = '...'
,#UID VARCHAR(255) = '...'
,#Name VARCHAR(255) = 'Master'
DECLARE #connect varchar(max) = '''Server=' + #ServerName + ';database=' + #DatabaseName + ';Uid=' + #UID + ';Pwd=' + #Pwd + ';'''
declare #sqlstring varchar(max) = 'SET ANSI_NULLS OFF; SET ANSI_WARNINGS OFF;
SELECT CodeTypeID FROM EDDSDBO.Code WHERE Name = ''''' + #Name + ''''' '
declare #TheExecutableStr varchar(max) = '
SELECT *
FROM OPENROWSET(''SQLNCLI'', ' + #connect + ',
''' + #sqlstring + ''')'
SELECT #TheExecutableStr
--EXEC(#TheExecutableStr)
Please note you have to count single quote very diligently, hence SELECT #TheExecutableStr. It is for debugging purposes. You can view the query to be executed and actually copy/paste and execute before uncommenting last statement.
Hope it helps.
just noticed, you have sys.sp_executesql within sys.sp_executesql which might be problematic. you might need to find a way to refactor the code.

Related

Compare two table using dynamic database

I have 2 table (same name) on 2 different database
I want compare these table, this is my procedure
ALTER PROCEDURE COUNTCOLUMN
#TABLENAME NVARCHAR(MAX),
#DATABASENAME1 NVARCHAR(MAX),
#DATABASENAME2 NVARCHAR(MAX)
AS
BEGIN
DECLARE #COLNAME NVARCHAR(MAX)
DECLARE #ROWCOUNT INT
DECLARE #ROWCOUNT2 INT
DECLARE #NUMB1 INT
DECLARE #NUMB2 INT
DECLARE #SQLQUERY NVARCHAR(MAX)
DECLARE #SQLQUERY2 NVARCHAR(MAX)
DECLARE #SQLQUERY3 NVARCHAR(MAX)
DECLARE #SQLQUERY4 NVARCHAR(MAX)
DECLARE #SQLQUERY5 NVARCHAR(MAX)
DECLARE #SQLQUERY6 NVARCHAR(MAX)
SET #SQLQUERY = 'SELECT #NUMBER = COUNT(*) FROM information_schema.columns WHERE table_name = '+N'#TESTTB'+''
SET #SQLQUERY2 = 'USE ' + #DATABASENAME1
SET #SQLQUERY3 = 'USE ' + #DATABASENAME2
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY, N'#TESTTB NVARCHAR(MAX), #NUMBER INT OUTPUT',#TESTTB = #TABLENAME ,#NUMBER = #NUMB1 OUTPUT
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY, N'#TESTTB NVARCHAR(MAX), #NUMBER INT OUTPUT',#TESTTB = #TABLENAME ,#NUMBER = #NUMB2 OUTPUT
IF(#NUMB1 <= #NUMB2)
BEGIN
DECLARE #NUMB INT
SET #NUMB = 1
WHILE (#NUMB <= #NUMB1)
BEGIN
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'TEMP1'))
BEGIN
DROP TABLE TEMP1
END
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'TEMP2'))
BEGIN
DROP TABLE TEMP2
END
ELSE
BEGIN
SET #SQLQUERY4 = 'SELECT #COL_NAME = COL_NAME(OBJECT_ID(#TABLENAME), #NUMB)'
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY4, N'#TABLENAME NVARCHAR(MAX),#NUMB INT, #COL_NAME NVARCHAR(MAX) OUTPUT', #TABLENAME = #TABLENAME,#NUMB=#NUMB, #COL_NAME = #COLNAME OUTPUT
SET #SQLQUERY5 = 'SELECT '+ #COLNAME + ' INTO TEST_DB..TEMP1 FROM ' + #TABLENAME
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY5
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY4, N'#TABLENAME NVARCHAR(MAX),#NUMB INT, #COL_NAME NVARCHAR(MAX) OUTPUT', #TABLENAME = #TABLENAME,#NUMB=#NUMB, #COL_NAME = #COLNAME OUTPUT
SET #SQLQUERY6 = 'SELECT ' + #COLNAME +' INTO TEST_DB..TEMP2 FROM '+ #TABLENAME
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY6
SELECT #ROWCOUNT = COUNT(*) FROM TEMP1
SELECT #ROWCOUNT2 = COUNT(*) FROM TEMP2
IF(#ROWCOUNT != #ROWCOUNT2)
BEGIN
PRINT N'NUMBER OF ROWS ARE NOT EQUAL';
BREAK
END
ELSE IF (#NUMB > #NUMB1)
BREAK
ELSE
BEGIN
SELECT * FROM TEST_DB..TEMP1
EXCEPT
SELECT * FROM TEST_DB..TEMP2
SET #NUMB = #NUMB + 1;
CONTINUE
END
END
END
END
END
It should be like this TEMP1 <--TEST_DB..TESTTABLE, TEMP2 <--TEST_DB2..TESTTABLE
But TEMP1 , TEMP2 always have the same values as TESTTABLE from the first database (TEST_DB )
It seem like I can't switch between two databases in procedure so I try to run it separate but still not work

I want to use dynamic variable that is declared within dynamic SQL

declare #sql as nvarchar(500)=''
set #sql='
declare #N4 as int = 1
declare #ms as nvarchar(100) = concat(''ms'', convert(nvarchar(10), #N4))
select #ms
'
exec #sql
I want output as ms1.
DECLARE #SQL AS NVARCHAR(500)=''
SET #sql='
while (#i <10)
begin
PRINT (''MS_''+#I)
set #i=#i+1
end
'
EXEC(#SQL)
not generating value for #i
i want to put this code in while loop as I want to access ms1 to ms10
Use sp_executesql which supports ouput params
DECLARE #MS VARCHAR(50)
exec sp_executesql N'declare #N4 as int = 1;
SELECT #MS= concat(''ms'', convert(nvarchar(10), #N4))',
N'#MS VARCHAR(50) output', #MS output;
SELECT #MS
Yes, you can use and for that you need to use sp_executesql like this -
Declare #sql as nvarchar(500)='', #Params NVARCHAR(500),
#N4 Int = 1, #ms nvarchar(100)
SET #Params = '#N4 Int, #ms nvarchar(100) OUTPUT'
set #sql= N'SELECT #ms = concat(''ms'', convert(nvarchar(10), #N4))'
EXEC SP_EXECUTESQL #sql, #Params, #N4 = #N4, #ms = #ms OUTPUT
SELECT #ms
Use While statement and string concatenation to get your result :
DECLARE #StartValue INT = 1
DECLARE #EndValue INT = 10
DECLARE #Query VARCHAR(500) = ''
WHILE #StartValue < #EndValue
BEGIN
SET #Query = #Query + 'sms_' + CAST(#StartValue AS VARCHAR) + ','
SET #StartValue = #StartValue + 1
END
SELECT Query

Variable in a dynamic query

I have a problem once I want to run the next query:
declare #j int = 1;
declare #column varchar(255);
set #column = 'last_name';
declare #tmp varchar(255);
declare #query nvarchar(255) = N'select ' + #column + N' from TEST where id = #j'
declare #tbl table(tmp varchar(255))
insert into #tbl
exec sp_executesql #query
select top 1 #tmp = tmp from #tbl
select #tmp
select * from #tbl;
The problem is that if I change the variable #j to a numeric value in the declaration of the #query variable, like this
declare #query nvarchar(255) = N'select ' + #column + N' from TEST where id = 1'
the query is running successfully, if I left the #j variable there, like this
declare #query nvarchar(255) = N'select ' + #column + N' from TEST where id = #j'
I got an error message:
"Must declare the scalar variable #j."
Why? And how can I solve that my query would work with the variable #j?
in the place of ==== where id = #j'
change like this may it works
Convert(nvarchar(2), #j);
You need to cast the #j variable to string. AS its type is INT you should cast it as
CAST(#j AS VARCHAR(12))
Also, you can pass parameters to dynamic SQL statement when it is executed using sp_executesql. In your case it will be something like this:
declare #j int = 1;
declare #column varchar(255);
set #column = 'last_name';
declare #tmp varchar(255);
declare #query nvarchar(255) = N'select ' + #column + N' from TEST where id = #j'
declare #tbl table(tmp varchar(255))
insert into #tbl
exec sp_executesql #query, N'#j INT', #j = #j
select top 1 #tmp = tmp from #tbl
select #tmp
select * from #tbl;
In the following line:
exec sp_executesql #query, N'#j INT', #j = #j
#query is your dynamic T-SQL statement
N'#j INT' is declaration of parameters
#j = #j is parameters assignments
sp_executsql documentation
You can use parameters in the sp_executesql statement like this:
CREATE TABLE TempTable (
TempID INT IDENTITY(1,1) NOT NULL,
SomeDescription VARCHAR(255) NOT NULL,
PRIMARY KEY(TempID))
INSERT INTO TempTable (SomeDescription)
VALUES ('Description 1'),
('Description 2'),
('Description 3')
DECLARE #sql NVARCHAR(500) = 'SELECT * FROM TempTable WHERE TempID = #TempVar',
#params NVARCHAR(500) = '#TempVar int',
#j INT = 2;
EXEC sp_executesql #sql, #params, #TempVar = #j;
declare #j int = 1;
declare #column varchar(255);
set #column = 'last_name';
declare #tmp varchar(255);
declare #query nvarchar(255) = N'select ' + #column + N' from TEST where id ='+ CAST(#j AS VARCHAR(5))
declare #tbl table(tmp varchar(255))
insert into #tbl
exec (#query)
select top 1 #tmp = tmp from #tbl
select #tmp
select * from #tbl;
enter code here

What is the best approach to filter data in SQL (Dynamic Query)?

I have a stored procedure which is supposed to return a result set from a table filtered according to parameters provided.
UPDATE
alter procedure Proc_CheckExchange
#Flag varchar(3),
#symbol varchar(13)=null,
#exchange char(3)=null,
#limit money=null,
#chargerate numeric(18,4)=null,
#ChgType char(2)=null,
#IsActive int=null,
#Mkrid varchar(11)=null,
#statecode varchar(4) =null
as
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if(#Flag='CHK')
begin
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N'and exchange=#exchange'+ cast(#exchange as nvarchar(100))
if len(isnull(#symbol, '')) > 0
set #sql = #sql + N'and symbol=#symbol'+ cast(#symbol as nvarchar(100))
if len(isnull(#limit, '')) > 0
set #sql = #sql + N'and limit=#limit'+ cast(#limit as nvarchar(100))
if len(isnull(#chargerate, '')) > 0
set #sql = #sql + N'and chargerate=#chargerate'+ cast(#chargerate as nvarchar(100))
if len(isnull(#ChgType, '')) > 0
set #sql = #sql + N'and ChgType=#ChgType'+ cast(#ChgType as nvarchar(100))
if len(isnull(#IsActive, '')) > 0
set #sql = #sql + N'and IsActive=#IsActive'+ cast(#IsActive as nvarchar(100))
if len(isnull(#statecode, '')) > 0
set #sql = #sql + N'and statecode=#statecode'+ cast(#statecode as nvarchar(100))
exec (#sql)
end
if (#Flag='ALL')
begin
select * from Tbl_StampDutyException1
end
UPDATE 1
alter procedure Proc_CheckExchange
#Flag varchar(3),
#symbol varchar(13)=null,
#exchange char(3)=null,
#limit money=null,
#chargerate numeric(18,4)=null,
#ChgType char(2)=null,
#IsActive int=null,
#Mkrid varchar(11)=null,
#statecode int =null
as
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if(#Flag='CHK')
begin
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = #exchange'
if len(isnull(#limit, '')) > 0
set #sql = #sql + N' and limit = #limit'
if len(isnull(#chargerate, '')) > 0
set #sql = #sql + N' and chargerate = #chargerate'
if len(isnull(#ChgType, '')) > 0
set #sql = #sql + N' and ChgType = #ChgType'
if len(isnull(#IsActive, '')) > 0
set #sql = #sql + N' and IsActive = #IsActive'
if len(isnull(#statecode, '')) > 0
set #sql = #sql + N' and statecode = #statecode'
if len(isnull(#symbol, '')) > 0
set #sql = #sql + N' and symbol = #symbol'
declare #params as nvarchar(max) = N'#Flag varchar(3),
#symbol varchar(13),
#exchange char(3),
#limit money,
#chargerate numeric(18,4),
#ChgType char(2),
#IsActive int,
#Mkrid varchar(11),
#statecode varchar(4)'
print #sql
--EXECUTE sp_executesql #sql, #params, #Flag, #symbol, #exchange, #limit, #chargerate, #ChgType, #IsActive, #Mkrid, #statecode
end
I am trying to create a stored procedure in which there will be as many conditions in WHERE clause as passed to the stored procedure. I hope I am clear about what I am trying to achieve. I am getting error Error converting data type varchar to numeric.
I think that a good solution would be this one
SELECT * from dbo.Clients
WHERE
(#param1 IS NULL OR field1 = #param1)
AND (#param2 IS NULL OR field2 = #param2)
With this approach the statement will not be re-processed (execution plan) by the server every time you execute the query.
What you could do is to rewrite your stored proc using dynamic SQL and include parts of where clause only if parameters are defined, e.g. replace the body with
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = ' + cast(#exchange as nvarchar(100))
-- add all parameters; you need to cast them to nvarchar if they have other type
exec (#sql)
As an improvement, you can use sp_executesql to execute dynamic SQL. See here on how to use it. In this case, the code will be:
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = #exchange'
-- add all parameters;
declare #params as nvarchar(max) = N'#Flag varchar(3),
#symbol varchar(13),
#exchange char(3),
#limit money,
#chargerate numeric(18,4),
#ChgType char(2),
#IsActive int,
#Mkrid varchar(11),
#statecode varchar(4)'
EXECUTE sp_executesql #sql, #params, #Flag, #symbol, #exchange, #limit, #chargerate, #ChgType, #IsActive, #Mkrid, #statecode
By the way, don't use select * in stored procedures, it's not a good practice. List all the columns you want to return. Otherwise, if the table definition changes, you will get different result to what it was previously.
What I guess you need is a CONDITIONAL WHERE CLAUSE approach, have provided the sample script below, you need to convert all the IF conditions to a CASE and you then you can combine all the conditions into a single SELECT statement:
SELECT * FROM Tbl_StampDutyException1
WHERE
exchange = CASE WHEN #exchange='' or #exchange is null THEN exchange ELSE #exchange END
AND symbol = Case when #symbol='' or #symbol is null THEN symbol ELSE symbol
AND *so on.....*

how to use print statement when you have more than 8000 characters

I have a procedure and the code looks like this:
ALTER PROCEDURE [dbo].[usp_Gen_Proc]
(#ID INT )
AS
Begin
IF OBJECT_ID('tempdb..#procedure') IS NOT NULL
DROP TABLE #procedure
DECLARE #Name VARCHAR(100)
DECLARE #SQL VARCHAR(MAX)
DECLARE #Script VARCHAR(MAX),
#DB VARCHAR(100),
#Schema VARCHAR(100),
#Proc_Name VARCHAR(max),
#BR CHAR(2)
SET #BR = CHAR(10)+ CHAR(13)
SET NOCOUNT ON
SELECT #Name= [Procedure_Name] FROM dbo.datsource
WHERE [ID] = #ID
SELECT #Proc_Name = PARSENAME(#Name, 1) ,
#Schema = PARSENAME(#Name, 2) ,
#DB = PARSENAME(#Name, 3)
CREATE TABLE #procedure ( script VARCHAR(MAX) )
EXEC('INSERT INTO #procedure(script)
SELECT definition FROM '+#DB+'.sys.all_sql_modules sq
WHERE sq.object_id = (SELECT object_id FROM '+#DB+'.sys.objects
WHERE type = ''P'' AND name = '''+#Proc_Name+''' ) ')
SET #Script = ' Use ' +#BR +#DB + #BR+ ' Go' +#BR
SELECT #Script = #Script+script
FROM #procedure
DECLARE #pos INT =7500
SELECT #pos=CHARINDEX(CHAR(13)+CHAR(10),#script,#pos)
PRINT SUBSTRING(#Script,1,#Pos)
DECLARE #Counter INT
SET #Counter = 0
DECLARE #TotalPrints INT
SET #TotalPrints = ( LEN(#script) / 8000 )
WHILE #Counter < #TotalPrints
BEGIN
SET #Counter = #Counter + 1
PRINT SUBSTRING(#script,#pos+1,7500)
SET #pos = #pos+7500
SELECT #pos=CHARINDEX(CHAR(13)+CHAR(10),#script,#pos)
END
END
Basically what the procedure is doing it just prints the procedure code. But the problem I am facing is when I am printing it i am loosing some characters in between.
Can anyone let me know where I am going wrong?
Why do you want to print ?
Why you don't do a select with the script, put it in xml if you want to see everything.
Select Convert(xml, #script)
EDIT :
between you don't need to do +1
PRINT SUBSTRING(#script,#pos,7500)