Related
I use table type param (#listdraftIds) and split data in table to compare with data in DB but the error is as below. Help me, thank you.
CREATE OR ALTER PROCEDURE [dbo].[sp_sel_contract]
(
#listDraftId nvarchar(max),
#Page int,
#PageNumber int,
#TotalRecord int OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
BEGIN
DECLARE #listdraftIds TABLE (draftIds nvarchar(max))
INSERT INTO #listdraftIds VALUES(#listDraftId)
DECLARE #sql varchar(MAX)
SET #sql= ' ALTER DATABASE '+ quotename(db_name()) + ' SET COMPATIBILITY_LEVEL = 130 ';
EXEC(#sql)
END
DECLARE #query NVARCHAR(MAX)
DECLARE #newquery NVARCHAR(MAX)
DECLARE #RowIndex int
SET #RowIndex = (#Page-1) * #PageNumber
SET #query = ''
SET #query = #query + ' SELECT DISTINCT'
SET #query = #query + ' [d].[draft_id], '
...
SET #query = #query + 'WHERE '
SET #query = #query + ' [d].[del_flg] = ''FALSE'''
SET #query = #query + 'AND '
SET #query = #query + ' [d].[draft_id] '
SET #query = #query + 'IN ('
SET #query = #query + ' SELECT DISTINCT '
SET #query = #query + ' value AS draft_id '
SET #query = #query + 'FROM '
SET #query = #query + ' #listdraftIds '
SET #query = #query + 'CROSS APPLY STRING_SPLIT(draftIds, '','') '
SET #query = #query + 'WHERE '
SET #query = #query + ' RTRIM(value) <> '''' )'
PRINT #query
SET #newquery = ' SET #TotalRecord = (SELECT COUNT(*) FROM (' + #query +') AS t) '
SET #newquery = #newquery + ' ORDER BY '
SET #newquery = #newquery + ' [draft_date] DESC, [d].[draft_id] DESC, [g].[detail_no] ASC'
SET #newquery = #newquery + ' OFFSET #RowIndex ROWS FETCH NEXT #PageNumber ROWS ONLY'
EXECUTE sp_executesql #newquery,
#listDraftId = #listDraftId,
#RowIndex = #RowIndex,
#PageNumber = #PageNumber,
#TotalRecord = #TotalRecord OUTPUT
END
Then Exec proc:
DECLARE #return_value int,
#TotalRecord int
EXEC #return_value = [dbo].[sp_sel_draft_condition_api1]
#listDraftId = N'123,345',
#Page = 1,
#PageNumber = 20,
#TotalRecord = #TotalRecord OUTPUT
SELECT #TotalRecord as N'#TotalRecord'
SELECT 'Return Value' = #return_value
GO
Error:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '30302'.
Msg 1087, Level 15, State 2, Line 3
Must declare the table variable "#listdraftIds".
Msg 137, Level 15, State 2, Line 3
Must declare the scalar variable "#PageNumber".
few issue here with the stored procedure
First, sp_executesql execute in its own context. So variable declared outside of it is not recognized. Which means, if you need to use a variable, you need to pass it into sp_executesql.
Your sp_executesql statement does not defined any variable
it should be
EXECUTE sp_executesql #newquery,
N'#RowIndex int, #PageNumber int, #TotalRecord int OUTPUT',
#RowIndex = #RowIndex,
#PageNumber = #PageNumber,
#TotalRecord = #TotalRecord OUTPUT
Note that I didn't include the #listDraftId. That is because you can't pass a table variable into sp_executesql. One alternative is to use a temp table
So in your sp_sel_contract
create table #listdraftId ( draftIds nvarchar(max) )
And then you can reference the temp table #listdraftId in you dynamic query
Actually looking at your dynamic query, you don't required a table variable or temp table. You only have one value of #listDraftId that is pass into sp_sel_contract and then it is pass into the dynamic query. So you can pass that variable directly into the dynamic query. Example :
EXECUTE sp_executesql #newquery,
N'#listDraftId nvarchar(max), #RowIndex int, #PageNumber int, #TotalRecord int OUTPUT',
#listDraftId = #listDraftId,
#RowIndex = #RowIndex,
#PageNumber = #PageNumber,
#TotalRecord = #TotalRecord OUTPUT
EDIT :
Now i have a closer look at your dynamic query ? I don't see a need to use dynamic query at all. You can achieve what you want with normal query
I am trying to execute this query:
declare #tablename varchar(50)
set #tablename = 'test'
select * from #tablename
This produces the following error:
Msg 1087, Level 16, State 1, Line 5
Must declare the table variable "#tablename".
What's the right way to have the table name populated dynamically?
For static queries, like the one in your question, table names and column names need to be static.
For dynamic queries, you should generate the full SQL dynamically, and use sp_executesql to execute it.
Here is an example of a script used to compare data between the same tables of different databases:
Static query:
SELECT * FROM [DB_ONE].[dbo].[ACTY]
EXCEPT
SELECT * FROM [DB_TWO].[dbo].[ACTY]
Since I want to easily change the name of table and schema, I have created this dynamic query:
declare #schema sysname;
declare #table sysname;
declare #query nvarchar(max);
set #schema = 'dbo'
set #table = 'ACTY'
set #query = '
SELECT * FROM [DB_ONE].' + QUOTENAME(#schema) + '.' + QUOTENAME(#table) + '
EXCEPT
SELECT * FROM [DB_TWO].' + QUOTENAME(#schema) + '.' + QUOTENAME(#table);
EXEC sp_executesql #query
Since dynamic queries have many details that need to be considered and they are hard to maintain, I recommend that you read: The curse and blessings of dynamic SQL
Change your last statement to this:
EXEC('SELECT * FROM ' + #tablename)
This is how I do mine in a stored procedure. The first block will declare the variable, and set the table name based on the current year and month name, in this case TEST_2012OCTOBER. I then check if it exists in the database already, and remove if it does. Then the next block will use a SELECT INTO statement to create the table and populate it with records from another table with parameters.
--DECLARE TABLE NAME VARIABLE DYNAMICALLY
DECLARE #table_name varchar(max)
SET #table_name =
(SELECT 'TEST_'
+ DATENAME(YEAR,GETDATE())
+ UPPER(DATENAME(MONTH,GETDATE())) )
--DROP THE TABLE IF IT ALREADY EXISTS
IF EXISTS(SELECT name
FROM sysobjects
WHERE name = #table_name AND xtype = 'U')
BEGIN
EXEC('drop table ' + #table_name)
END
--CREATES TABLE FROM DYNAMIC VARIABLE AND INSERTS ROWS FROM ANOTHER TABLE
EXEC('SELECT * INTO ' + #table_name + ' FROM dbo.MASTER WHERE STATUS_CD = ''A''')
Use:
CREATE PROCEDURE [dbo].[GetByName]
#TableName NVARCHAR(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #sSQL nvarchar(500);
SELECT #sSQL = N'SELECT * FROM' + QUOTENAME(#TableName);
EXEC sp_executesql #sSQL
END
You can't use a table name for a variable. You'd have to do this instead:
DECLARE #sqlCommand varchar(1000)
SET #sqlCommand = 'SELECT * from yourtable'
EXEC (#sqlCommand)
You'll need to generate the SQL content dynamically:
declare #tablename varchar(50)
set #tablename = 'test'
declare #sql varchar(500)
set #sql = 'select * from ' + #tablename
exec (#sql)
Use sp_executesql to execute any SQL, e.g.
DECLARE #tbl sysname,
#sql nvarchar(4000),
#params nvarchar(4000),
#count int
DECLARE tblcur CURSOR STATIC LOCAL FOR
SELECT object_name(id) FROM syscolumns WHERE name = 'LastUpdated'
ORDER BY 1
OPEN tblcur
WHILE 1 = 1
BEGIN
FETCH tblcur INTO #tbl
IF ##fetch_status <> 0
BREAK
SELECT #sql =
N' SELECT #cnt = COUNT(*) FROM dbo.' + quotename(#tbl) +
N' WHERE LastUpdated BETWEEN #fromdate AND ' +
N' coalesce(#todate, ''99991231'')'
SELECT #params = N'#fromdate datetime, ' +
N'#todate datetime = NULL, ' +
N'#cnt int OUTPUT'
EXEC sp_executesql #sql, #params, '20060101', #cnt = #count OUTPUT
PRINT #tbl + ': ' + convert(varchar(10), #count) + ' modified rows.'
END
DEALLOCATE tblcur
You need to use the SQL Server dynamic SQL:
DECLARE #table NVARCHAR(128),
#sql NVARCHAR(MAX);
SET #table = N'tableName';
SET #sql = N'SELECT * FROM ' + #table;
Use EXEC to execute any SQL:
EXEC (#sql)
Use EXEC sp_executesql to execute any SQL:
EXEC sp_executesql #sql;
Use EXECUTE sp_executesql to execute any SQL:
EXECUTE sp_executesql #sql
Declare #tablename varchar(50)
set #tablename = 'Your table Name'
EXEC('select * from ' + #tablename)
Also, you can use this...
DECLARE #SeqID varchar(150);
DECLARE #TableName varchar(150);
SET #TableName = (Select TableName from Table);
SET #SeqID = 'SELECT NEXT VALUE FOR ' + #TableName + '_Data'
exec (#SeqID)
Declare #fs_e int, #C_Tables CURSOR, #Table varchar(50)
SET #C_Tables = CURSOR FOR
select name from sysobjects where OBJECTPROPERTY(id, N'IsUserTable') = 1 AND name like 'TR_%'
OPEN #C_Tables
FETCH #C_Tables INTO #Table
SELECT #fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '#C_Tables'
WHILE ( #fs_e <> -1)
BEGIN
exec('Select * from ' + #Table)
FETCH #C_Tables INTO #Table
SELECT #fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '#C_Tables'
END
I am getting an error running the code below. I believe it as to do with the number of single quotes used.
How can I set a variable inside OPENQUERY command?
#declare #myStr varchar(6)
set #myStr = 'Test1'
SELECT *
FROM OPENQUERY("192.168.1.1",'SET FMTONLY OFF; EXEC spNewTest #Param1 = ''+ #myStr +''')
Click to see the error message
Regards,
Elio Fernandes
If you want to create a single quote within a string you have to put in two single quotes. For example:
'''this is a string''' would be a string containing the following:
'this is a string'
So for your problem, you have to change your code to this:
#declare #myStr varchar(6)
set #myStr = 'Test1'
SELECT *
FROM OPENQUERY("192.168.1.1",'SET FMTONLY OFF; EXEC spNewTest #Param1 = '''+ #myStr +'''')
I just found the answer for my question, so I thought sharing it with you.
DECLARE #QUERY VARCHAR(MAX)
DECLARE #TSQL VARCHAR(100)
DECLARE #SP VARCHAR(50)
DECLARE #PARAMETERS VARCHAR(MAX)
DECLARE #PARAM1 VARCHAR(50)
DECLARE #PARAM2 VARCHAR(50)
SET #TSQL = N'SELECT * FROM OPENQUERY([192.168.1.1], ''SET FMTONLY OFF; '
SET #SP = 'EXEC spNewTest '
SET #PARAM1 = '#Type='''+ QUOTENAME('Test','''') + ''''
SET #PARAM2 = '#Year='''+ QUOTENAME('2016','''') + ''''
SET #PARAMETERS = #PARAM1 + ', ' + #PARAM2
SET #QUERY = #TSQL + #SP + #PARAMETERS + ''')'
EXECUTE (#QUERY)
Thanks
DECLARE #TablesList TABLE
(
TableName VARCHAR(500),
RefTable VARCHAR(500),
RefTableIDColumn VARCHAR(500)
)
DECLARE #Query AS VARCHAR(MAX)
SET #Query = 'DECLARE #badIds AS VARCHAR(500) DECLARE #TableXML AS XML'
SET #Query = #Query + ' SELECT #TableXML = xCol FROM (SELECT * FROM OPENROWSET (BULK ''\\10.0.0.60\Temp\path\DataItemTables.xml'', SINGLE_CLOB)AS xCol) AS R(xCol)'
SET #Query = #Query + ' INSERT INTO #TablesList SELECT ref.value(''tablename[1]'',''nvarchar(500)'') AS tablename,'
SET #Query = #Query + ' ref.value(''refTable[1]'',''nvarchar(500)'') AS refTable, ref.value(''refTableIDColumn[1]'',''nvarchar(500)'') AS refTableIDColumn FROM'
SET #Query = #Query + ' #TableXML.nodes(''//Table[#name="Description"]'') AS R(ref)'
SET #Query = #Query +'select * from #TablesList'
EXEC(#Query)
I am executing the above script. But I am getting an error as below
Msg 1087, Level 15, State 2, Line 1
Must declare the table variable "#TablesList".
Msg 1087, Level 15, State 2, Line 1
Must declare the table variable "#TablesList".
What I am doing wrong. But when I write the query in dynamic form like below , it works fine. The problem is that I want to remove all the dynamic portion of the SP .
DECLARE #Query AS VARCHAR(MAX)
SET #Query ='DECLARE #TablesList TABLE ( TableName VARCHAR(500),RefTable VARCHAR(500),RefTableIDColumn VARCHAR(500))'
SET #Query = #Query + ' DECLARE #badIds AS VARCHAR(500) DECLARE #TableXML AS XML'
SET #Query = #Query + ' SELECT #TableXML = xCol FROM (SELECT * FROM OPENROWSET (BULK ''\\10.0.0.60\Temp\Path\DataItemTables.xml'', SINGLE_CLOB)AS xCol) AS R(xCol)'
SET #Query = #Query + ' INSERT INTO #TablesList SELECT ref.value(''tablename[1]'',''nvarchar(500)'') AS tablename,'
SET #Query = #Query + ' ref.value(''refTable[1]'',''nvarchar(500)'') AS refTable, ref.value(''refTableIDColumn[1]'',''nvarchar(500)'') AS refTableIDColumn FROM'
SET #Query = #Query + ' #TableXML.nodes(''//Table[#name="Description"]'') AS R(ref)'
SET #Query = #Query +'select * from #TablesList'
EXEC(#Query)
You can only access a table variable in the same scope where it is declared. Since your EXEC is in a different scope, the table variable is not recognized. One way to solve this is to use a temp table instead:
CREATE TABLE #TablesList
(
TableName VARCHAR(500),
RefTable VARCHAR(500),
RefTableIDColumn VARCHAR(500)
)
DECLARE #Query AS VARCHAR(MAX)
SET #Query = 'DECLARE #badIds AS VARCHAR(500) DECLARE #TableXML AS XML'
SET #Query = #Query + ' SELECT #TableXML = xCol FROM (SELECT * FROM OPENROWSET (BULK ''\\10.0.0.60\Temp\path\DataItemTables.xml'', SINGLE_CLOB)AS xCol) AS R(xCol)'
SET #Query = #Query + ' INSERT INTO #TablesList SELECT ref.value(''tablename[1]'',''nvarchar(500)'') AS tablename,'
SET #Query = #Query + ' ref.value(''refTable[1]'',''nvarchar(500)'') AS refTable, ref.value(''refTableIDColumn[1]'',''nvarchar(500)'') AS refTableIDColumn FROM'
SET #Query = #Query + ' #TableXML.nodes(''//Table[#name="Description"]'') AS R(ref)'
SET #Query = #Query +'select * from #TablesList'
EXEC(#Query)
The following stored procedure works correctly execpt when I pass in the #NameSubstring parameter. I know I am not dynamically building the like clause properly. How can I build the like clause when this parameter also needs to be passed as a parameter in the EXEC sp_executesql call near the bottom of the procedure?
ALTER PROCEDURE [dbo].[spGetAutoCompleteList]
(
#AutoCompleteID int,
#StatusFlag int,
#NameSubstring varchar(100),
#CompanyID int,
#ReturnMappings bit,
#ReturnData bit
)
AS
DECLARE #ErrorCode int,
#GetMappings nvarchar(500),
#Debug bit,
#Select AS NVARCHAR(4000),
#From AS NVARCHAR(4000),
#Where AS NVARCHAR(4000),
#Sql AS NVARCHAR(4000),
#Parms AS NVARCHAR(4000)
SET #ErrorCode = 0
SET #Debug = 1
BEGIN TRAN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF #AutoCompleteID IS NOT NULL OR #StatusFlag IS NOT NULL OR #NameSubstring IS NOT NULL
BEGIN
SET #Select = '
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments'
SET #GetMappings = '
Select ac.AutoCompleteID'
IF #ReturnData = 1
BEGIN
SET #Select = #Select + '
, ac.AutoCompleteData'
END
SET #From = '
FROM tbAutoComplete ac'
SET #Where = '
WHERE 1=1'
IF #AutoCompleteID IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.AutoCompleteID = CAST(#AutoCompleteID AS nvarchar)'
END
IF #StatusFlag IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.StatusFlag = CAST(#StatusFlag AS nvarchar)'
END
IF #NameSubstring IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.AutoCompleteName like #NameSubstring' + '%'
END
SET #Where = #Where + '
AND ac.CompanyID = + CAST(#CompanyID AS nvarchar)'
SET #Sql = #Select + #From + #Where
SET #Parms = '
#AutoCompleteID int,
#StatusFlag int,
#NameSubstring varchar(100),
#CompanyID int'
EXEC sp_executesql #Sql,
#Parms,
#AutoCompleteID,
#StatusFlag,
#NameSubstring,
#CompanyID
IF #ReturnMappings = 1
BEGIN
SET #GetMappings = 'Select * FROM tbAutoCompleteMap acm WHERE acm.AutoCompleteID IN(' + #GetMappings + #From + #Where + ')'
--EXEC sp_executesql #GetMappings
END
IF #Debug = 1
BEGIN
PRINT #GetMappings
PRINT #Sql
END
END
SELECT #ErrorCode = #ErrorCode + ##ERROR
IF #ErrorCode <> 0
BEGIN
SELECT '<FaultClass>1</FaultClass><FaultCode>1</FaultCode>'
+ '<FaultDesc>Internal Database Error.</FaultDesc>'
+ '<FaultDebugInfo>(spGetAutoCompleteList): There was an error while trying to SELECT from tbAutoComplete.</FaultDebugInfo>'
ROLLBACK TRAN
RETURN
END
COMMIT TRAN
#NameString needs to be outside of the quotes. To get #NameString% enclosed in quotes, you use two single quotes to escape the quote character as a literal.
SET #Where = #Where + '
AND ac.AutoCompleteName like ''' + #NameSubstring + '%'''
To avoid SQL injection, do not use concatenation when adding the parameter to your SQL statement. I strongly recommend that you use this format:
IF #NameSubstring IS NOT NULL BEGIN
SET #Where += 'AND ac.AutoCompleteName LIKE #NameSubstring + char(37)'
END
By using char(37) instead of '%' you avoid having to escape the apostrophes around the string literal
If you wanted to put a wildcard at either side, then you would use
IF #NameSubstring IS NOT NULL BEGIN
SET #Where += 'AND ac.AutoCompleteName LIKE char(37) + #NameSubstring + char(37)'
END
-----------------------------------------------------------------------------
In case someone believes I am wrong, here's proof that concatenation is a risk.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestInjection]') AND type in (N'U')) BEGIN
create table TestInjection(ID int, Value nvarchar(10))
insert into TestInjection (ID,Value)
Values
(1,'Tom'),
(2,'Fred'),
(3,'Betty'),
(4,'Betty2'),
(5,'Betty3'),
(6,'George')
END
declare #NameSubstring nvarchar(1000) = 'Bet'
--declare #NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare #ID int = 2
Declare #sql nvarchar(1000) = 'select * from TestInjection where ID > #ID '
SET #sql +=' AND [Value] like ''' + #NameSubstring + '%'''
Declare #params nvarchar(100) = '#ID int'
exec sp_executesql #sql, #params, #ID
select * from TestInjection
Run it the first time and you will get a resultset with 3 records, and another with all 6 records.
Now swap the declaration of #NameSubstring to the alternative, and re-run. All data in the table has been deleted.
If on the other hand you write your code like:
declare #NameSubstring nvarchar(1000) = 'Bet'
--declare #NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare #ID int = 2
Declare #sql nvarchar(1000) = 'select * from TestInjection where ID > #ID '
SET #sql +=' AND [Value] LIKE #NameSubstring + char(37)'
Declare #params nvarchar(100) = '#ID int, #NameSubstring nvarchar(1000)'
exec sp_executesql #sql, #params, #ID, #NameSubstring
select * from TestInjection
Then you still get the 3 records returned the first time, but you don't lose your data when you change the declaration.
SET #Where = #Where + 'AND ac.AutoCompleteName like ''%' + #NameSubstring + '%'''
So, you are asking how to specify parameters when you use dynamic queries and sp_executesql ?
It can be done, like this:
DECLARE /* ... */
SET #SQLString = N'SELECT #LastlnameOUT = max(lname) FROM pubs.dbo.employee WHERE job_lvl = #level'
SET #ParmDefinition = N'#level tinyint, #LastlnameOUT varchar(30) OUTPUT'
SET #IntVariable = 35
EXECUTE sp_executesql #SQLString, #ParmDefinition, #level = #IntVariable, #LastlnameOUT=#Lastlname OUTPUT
You can read more about it here: http://support.microsoft.com/kb/262499
Perhaps this wouldn't be an issue if you weren't using dynamic SQL. It looks to me like a vanilla query would work just as well and be much more straightforward to read and debug. Consider the following:
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments
FROM tbAutoComplete ac
WHERE ((ac.AutoCompleteID = CAST(#AutoCompleteID AS nvarchar) OR (#AutoCompleteID IS NULL))
AND ((ac.StatusFlag = CAST(#StatusFlag AS nvarchar)) OR (#StatusFlag IS NULL))
AND ((ac.AutoCompleteName like #NameSubstring + '%') OR (#NameSubstring IS NULL))
AND ((ac.CompanyID = CAST(#CompanyID AS nvarchar)) OR (#CompanyID IS NULL))
This is much simpler, clearer etc. Good luck!