I'm trying to write a stored procedure in SQL Server that gets the columns as parameters. The user will select the column name from a combo box and will write the searched value for that column on a textbox.
I've been searching how to do this and so far i have this:
ALTER PROCEDURE [dbo].[SP_Select_TBL_Folio]
#cant int,
#Column1 nvarchar(50),
#Value1 nvarchar(50),
#Column2 nvarchar(50),
#Value2 nvarchar(50),
#Column3 nvarchar(50),
#Value3 nvarchar(50)
AS
BEGIN
declare #query nvarchar (max)
SET NOCOUNT ON;
if #cant = 1
BEGIN
set #query = 'SELECT * FROM TBL_Folio WHERE ' + #Column1 + ' LIKE '+ #Value1 + ' ORDER BY 1 DESC';
exec sp_executesql #query, N' '
END
else
BEGIN
if #cant = 2
BEGIN
set #query = 'SELECT * FROM TBL_Folio WHERE ' + #Column1 + ' LIKE '+ #Value1 + ' AND ' + #Column2 + ' LIKE '+ #Value2 + ' ORDER BY 1 DESC';
exec sp_executesql #query, N' '
END
ELSE
if #cant = 3
BEGIN
set #query = 'SELECT * FROM TBL_Folio WHERE ' + #Column1 + ' LIKE '+ #Value1 + ' AND ' + #Column2 + ' LIKE '+ #Value2 + ' AND ' + #Column3 + ' LIKE '+ #Value3 + ' ORDER BY 1 DESC';
exec sp_executesql #query, N' '
END
END
END
The user can send 1 to 3 values, for that I have the parameter #cant, this code works but I want to know if there is a better way to do this or how can I improve this stored procedure.
I think what you have is fine if you need to do it in an SP rather than client side. I would probabably initialize the query to the 'select * from TBL_Folio" and then append the LIKES after each if. I would also caution against using SELECT * so your client side doesn't blow up if a field gets added to the table.
If you have a need to check a variable number of fields rather than just up to 3, you can do a table-valued parameter and build up your query by looping through. Here is an example:
ALTER PROCEDURE [dbo].[GetFilteredInvoices]
#FilterColumns ColumnValueType READONLY
AS
BEGIN
SET NOCOUNT ON;
declare #columnName varchar(50), #columnValue varchar(MAX), #query nvarchar(MAX), #count int
set #query='SELECT InvoiceNumber, InvoiceDate, Customer from Invoices '
set #count=0
set #columnName=''
while exists(select * from #FilterColumns where ColumnName>#ColumnName)
begin
set #columnName=(select min(ColumnName) from #FilterColumns where ColumnName>#columnName)
if #count=0
set #query=#query+'WHERE '
else
set #query=#query+'AND '
set #query=#query+ (select ColumnName+' Like ''%'+ColumnValue+'%'' ' from #filterColumns where ColumnName=#columnName)
set #count=#count+1
end
exec sp_executesql #query
END
Here is the table valued type I used:
CREATE TYPE [dbo].[ColumnValueType] AS TABLE(
[ColumnName] [varchar](50) NULL,
[ColumnValue] [varchar](max) NULL
)
GO
Now this will take any number of columns and values to apply the filter.
Here is an example call to the procedure:
DECLARE #RC int
DECLARE #FilterColumns [dbo].[ColumnValueType]
insert into #filterColumns
Values('InvoiceNumber','345')
,('Customer','67')
EXECUTE #RC = [dbo].[GetFilteredInvoices]
#FilterColumns
I think you can perhaps improve the way that you handle your input parameters by getting rid of the #cant parameter. You can also improve the way that you build up the conditions, at the moment you are not handling the situations where only #Column2 and #Value2 or only #Column3 and #Value3 is set (perhaps it is not needed in your case, but it is still good practice to handle these types of scenarios)
CREATE PROCEDURE SP_Select_TBL_Folio
#Column1 NVARCHAR(50) = NULL,
#Value1 NVARCHAR(50) = NULL,
#Column2 NVARCHAR(50) = NULL,
#Value2 NVARCHAR(50) = NULL,
#Column3 NVARCHAR(50) = NULL,
#Value3 NVARCHAR(50) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#P1 NVARCHAR(500),
#P2 NVARCHAR(500),
#P3 NVARCHAR(500),
#SQL NVARCHAR(MAX)
IF (#Column1 IS NULL OR #Column1 = '') AND (#Value1 IS NULL OR #Value1 = '')
BEGIN
-- This will build up dynamic SQL to always select records even if #Column1
-- and #Value1 is not set. This obvisously all depends on your requirements
-- and if you still need to select records if the parameters are not set, otherwise
-- it can be changed to ' WHERE ThePrimaryKeyColumn = 0'
SET #P1 = ' WHERE ThePrimaryKeyColumn > 0'
END
ELSE
BEGIN
SET #P1 = 'WHERE ' + #Column1 + ' LIKE ' + '''' + #Value1 + ''''
END
IF (#Column2 IS NULL OR #Column2 = '') AND (#Value2 IS NULL OR #Value2 = '')
BEGIN
SET #P2 = ''
END
ELSE
BEGIN
SET #P2 = ' AND ' + #Column2 + ' LIKE ' + '''' + #Value2 + ''''
END
IF (#Column3 IS NULL OR #Column3 = '') AND (#Value3 IS NULL OR #Value3 = '')
BEGIN
SET #P3 = ''
END
ELSE
BEGIN
SET #P3 = ' AND ' + #Column3 + ' LIKE ' + '''' + #Value3 + ''''
END
SET #SQL = 'SELECT * FROM TBL_Folio
[P1]
[P2]
[P3]'
-- Here we set all the conditions
SET #SQL = REPLACE(#SQL, '[P1]', #P1);
SET #SQL = REPLACE(#SQL, '[P2]', #P2);
SET #SQL = REPLACE(#SQL, '[P3]', #P3);
-- This will be replaced by EXEC(#SQL)
PRINT #SQL
END
So now you can for instance execute
EXEC SP_Select_TBL_Folio
which will give you
SELECT * FROM TBL_Folio
WHERE ThePrimaryKeyColumn > 0
or you can execute
EXEC SP_Select_TBL_Folio 'Column1','Value1'
which will give you
SELECT * FROM TBL_Folio
WHERE Column1 LIKE 'Value1'
or you can execute
EXEC SP_Select_TBL_Folio NULL,NULL,'Column2','Value2'
which will give you
SELECT * FROM TBL_Folio
WHERE ThePrimaryKeyColumn > 0
AND Column2 LIKE 'Value2'
I'm not going to list all the permutations, I'm sure you get my point.
I have an SQL exec in stored procedure like this
DECLARE
#SQLQuery NVARCHAR(max),
#table NVARCHAR(max)
SET #table = + 'log_'+ #idCampaign
SET #SQLQuery =
'DECLARE #ID NVARCHAR(200);
INSERT INTO ' + #table + ' ( campaignRowID, idCampaign, idUser )
VALUES( ''' + #campaignRowID + ''', ''' + #idCampaign + ''', ''' + #idUser + ''' )
SET #ID = SCOPE_IDENTITY();
exec InsertData '''+ convert(nvarchar, #idCampaign) + ''',''' + convert(nvarchar, #ID) + ''''
exec (#SQLQuery)
i can get the #ID but can't use it for another execute SP btw #ID is nvarchar,
How i can get the return ID and use it for another execute sp?
I'm guessing a bit because the question is lacking detail, but I think your issue may be with how you're attempting to use the #ID variable.
Because #ID is declared inside the scope of the dynamic SQL statement, it's only accessible inside that scope.
The problem might be that on this line, you're attempting to use #ID as if it were declared outside the scope of the dynamic SQL:
exec InsertData '''+ convert(nvarchar, #idCampaign) + ''',''' + convert(nvarchar, #ID) + ''''
Instead, reference #ID inside the scope of the statement:
exec InsertData '''+ convert(nvarchar, #idCampaign) + ''',#ID'
EDIT
I created the following to test the code; it appears to work for me
CREATE TABLE log_100 (id INT IDENTITY, campaignRowId VARCHAR(100), idCampaign VARCHAR(100), idUser VARCHAR(100))
GO
CREATE PROC InsertData
#idCampaign VARCHAR(100),
#id NVARCHAR(200)
AS
SELECT #idCampaign ic, #id id
GO
declare #idCampaign varchar(100) = '100'
declare #campaignRowID varchar(100) = '200'
declare #idUser varchar(100) = '200'
DECLARE
#SQLQuery NVARCHAR(max),
#table NVARCHAR(max)
SET #table = + 'log_'+ #idCampaign
SET #SQLQuery =
'DECLARE #ID NVARCHAR(200);
INSERT INTO ' + #table + ' ( campaignRowID, idCampaign, idUser )
VALUES( ''' + #campaignRowID + ''', ''' + #idCampaign + ''', ''' + #idUser + ''' )
SET #ID = SCOPE_IDENTITY();
exec InsertData '''+ convert(nvarchar, #idCampaign) + ''',#ID'
exec (#SQLQuery)
Here is my query based sp:
DECLARE #GLOBALPATIENTACCOUNT VARCHAR(25)
DECLARE #Page_Index BIGINT
DECLARE #Page_Size BIGINT
DECLARE #practice_code VARCHAR(20)
DECLARE #dateFrom VARCHAR(20)
DECLARE #dateTo VARCHAR(20)
DECLARE #startFrom VARCHAR(20)
DECLARE #startTo VARCHAR(20)
DECLARE #Total_Record DECIMAL(34, 1)
DECLARE #Total_Pages DECIMAL(34, 1)
DECLARE #From_row BIGINT
DECLARE #To_row BIGINT
SET #GLOBALPATIENTACCOUNT = '999090999510103196'
SET #Page_Index = 1
SET #Page_Size = 30
SET #practice_code = 9090999
SET #dateFrom = '09/13/2014'
SET #dateTo = '10/13/2014'
SET #startFrom = 'null'
SET #startTo = 'null'
DECLARE #sqlstr AS varchar (max)
set #sqlstr='';
SET #sqlstr =#sqlstr +N'select CREATED_DTAE, DOCUMENT_CATEGORY_ID, DESCRIPTION, DOCUMENT_NAME, SHOW_ON_WEB, VIEW_BY_PATIENT, VIEW_DATE, NO_OF_IMAGES, DOCUMENT_STATUS_ID, DOCUMENT_STATUS_DESCRIPTION,
PATIENT_DOCUMENT_ID,ISDICOM,PATIENT_ACCOUNT,DOCUMENT_INDEX, ASSIGNED_TO,
CONTENT_START_DATE, COMMENTS,CHART_ID,LAST_NAME,FIRST_NAME, CONTENT_END_DATE,CREATED_BY,[CREATED DATE], MODIFIED_BY,MODIFIED_DATE,DELETED,SOURCE_PATH,CONFIDENTIAL,
DOC_UPLOAD_NAME,DOC_UPLOAD_STATUS,IS_MISC_DOC,BUTTON,SIGNED,SIGNED_BY,SIGNED_DATE, PRACTICE_CODE
into #temptbl
FROM
(
SELECT ISNULL(CREATED_DTAE,'''')as CREATED_DTAE , PATIENT_DOCUMENTS.DOCUMENT_CATEGORY_ID, ISNULL(UPPER(DT.DESCRIPTION),'''')as DESCRIPTION,
ISNULL(PATIENT_DOCUMENTS.DOCUMENT_NAME,'''') as DOCUMENT_NAME,
ISNULL(PATIENT_DOCUMENTS.SHOW_ON_WEB,''0'') AS SHOW_ON_WEB, ISNULL(PATIENT_DOCUMENTS.VIEW_BY_PATIENT,''0'') AS VIEW_BY_PATIENT,
ISNULL(CONVERT(VARCHAR(10),PATIENT_DOCUMENTS.VIEW_DATE,101),'''') AS VIEW_DATE, ISNULL(PATIENT_DOCUMENTS.NO_OF_IMAGES,''0'') AS NO_OF_IMAGES,
ISNULL((CONVERT(VARCHAR(12), CASE PATIENT_DOCUMENTS.DOCUMENT_STATUS_ID WHEN 0 THEN NULL ELSE PATIENT_DOCUMENTS.DOCUMENT_STATUS_ID END )),'''') AS DOCUMENT_STATUS_ID,
ISNULL(UPPER(DOCUMENTS_STATUS.DOCUMENT_STATUS_DESCRIPTION),'''') DOCUMENT_STATUS_DESCRIPTION , PATIENT_DOCUMENTS.PATIENT_DOCUMENT_ID,
ISNULL(PATIENT_DOCUMENTS.ISDICOM,''0'') ISDICOM, PATIENT_DOCUMENTS.PATIENT_ACCOUNT, ISNULL(PATIENT_DOCUMENTS.DOCUMENT_INDEX,0) DOCUMENT_INDEX, ISNULL(PATIENT_DOCUMENTS.ASSIGNED_TO,'''') ASSIGNED_TO ,
CONVERT(VARCHAR,PATIENT_DOCUMENTS.CONTENT_START_DATE,101) CONTENT_START_DATE, REPLACE (PATIENT_DOCUMENTS.COMMENTS,CHAR(10),'' '') AS COMMENTS, PATIENT.CHART_ID, UPPER(PATIENT.LAST_NAME)LAST_NAME,UPPER(PATIENT.FIRST_NAME) FIRST_NAME ,
CONVERT(VARCHAR,PATIENT_DOCUMENTS.CONTENT_END_DATE,101) CONTENT_END_DATE, ISNULL(UPPER(PATIENT_DOCUMENTS.CREATED_BY),'''') CREATED_BY,
CONVERT(VARCHAR,CREATED_DTAE,101) +'' ''+ LTRIM(SUBSTRING(CONVERT(VARCHAR,(CONVERT(DATETIME,CREATED_DTAE)),109),13,5))+'' ''+ SUBSTRING(CONVERT(VARCHAR,(CONVERT(DATETIME,CREATED_DTAE)),109),25,2) AS [CREATED DATE],
ISNULL(UPPER(PATIENT_DOCUMENTS.MODIFIED_BY),'''') MODIFIED_BY,
ISNULL(CONVERT(VARCHAR,PATIENT_DOCUMENTS.MODIFIED_DATE,101),'''') +'' ''+ LTRIM(SUBSTRING(CONVERT(VARCHAR,(CONVERT(DATETIME,PATIENT_DOCUMENTS.MODIFIED_DATE)),109),13,5))+'' ''+ SUBSTRING(CONVERT(VARCHAR,(CONVERT(DATETIME,PATIENT_DOCUMENTS.MODIFIED_DATE)),109),25 ,2) AS MODIFIED_DATE,PATIENT_DOCUMENTS.DELETED,ISNULL(PATIENT_DOCUMENTS.SOURCE_PATH,'''') SOURCE_PATH,ISNULL(PATIENT_DOCUMENTS.CONFIDENTIAL,0) CONFIDENTIAL,ISNULL(PATIENT_DOCUMENTS.DOC_UPLOAD_NAME,'''') DOC_UPLOAD_NAME,
ISNULL(PATIENT_DOCUMENTS.DOC_UPLOAD_STATUS,'''') DOC_UPLOAD_STATUS,CONVERT(BIT,''FALSE'') IS_MISC_DOC,'''' AS BUTTON,
PATIENT_DOCUMENTS.Signed SIGNED, PATIENT_DOCUMENTS.Sign_by SIGNED_BY, PATIENT_DOCUMENTS.Sign_date SIGNED_DATE,
PATIENT.PRACTICE_CODE AS PRACTICE_CODE
FROM PATIENT, PATIENT_DOCUMENTS
LEFT OUTER JOIN DOCUMENTS_STATUS ON PATIENT_DOCUMENTS.DOCUMENT_STATUS_ID = DOCUMENTS_STATUS.DOCUMENT_STATUS_ID,
DOCUMENT_CATEGORIES DT
WHERE PATIENT_DOCUMENTS.DOCUMENT_CATEGORY_ID = DT.DOCUMENT_CATEGORY_ID
AND PATIENT_DOCUMENTS.PATIENT_ACCOUNT = PATIENT.PATIENT_ACCOUNT AND PATIENT.Patient_GlobalId= ''' + #GLOBALPATIENTACCOUNT +'''
AND ISNULL(PATIENT.DELETED,0) <> 1
AND ISNULL(PATIENT_DOCUMENTS.DELETED, 0) <> 1
AND PATIENT.PRACTICE_CODE <> ''' + #practice_code + '''
AND PATIENT_DOCUMENTS.confidential <> 1 '
--print #sqlstr
IF (#dateFrom <> 'null')
BEGIN
SET #sqlstr = #sqlstr + ' and CONVERT(VARCHAR,ISNULL(PATIENT_DOCUMENTS.CREATED_DTAE,''''),101) >= CONVERT(VARCHAR,isnull( ''' + #dateFrom + ''',''''),101) and CONVERT(VARCHAR,ISNULL(PATIENT_DOCUMENTS.CREATED_DTAE,''''),101) <= CONVERT(VARCHAR,ISNULL(''' + #dateTo + ''',''''),101)'
--print #sqlstr
END
IF(#startFrom <> 'null')
BEGIN
SET #sqlstr = #sqlstr + ' CONVERT(VARCHAR,ISNULL(PATIENT_DOCUMENTS.CONTENT_START_DATE,''''),101) >= CONVERT(VARCHAR,ISNULL(''' + #startFrom + ''',''''),101) and CONVERT(VARCHAR,ISNULL(PATIENT_DOCUMENTS.CONTENT_START_DATE,''''),101) <= CONVERT(VARCHAR,isnull(''' + #startTo + ''',''''),101)'
END
SET #sqlstr = #sqlstr + 'UNION ALL
SELECT ISNULL(DOCUMENTS.CREATED_DATE,'''') AS CREATED_DTAE, CATAGORY_TYPE_ID AS DOCUMENT_CATEGORY_ID, ISNULL(DOCUMENT_CATEGORIES.DESCRIPTION,'''') DESCRIPTION,
(SELECT TOP 1 DOCUMENTS_CATEGORY_MAIN_PATH FROM DOCUMENTS_CATEGORY_MAIN WHERE DOCUMENTS_CATEGORY_MAIN_DESCRIPTION=''ATTACHMENTS'' )+''\'' +DOCUMENTS.FILE_NAME_FORPATH AS [DOCUMENT_NAME], isnull(DOCUMENTS.SHOW_ON_WEB,0) AS SHOW_ON_WEB,''0'' AS VIEW_BY_PATIENT
,ISNULL(NULL,'''') AS VIEW_DATE, ''0'' AS NO_OF_IMAGES,ISNULL(DOCUMENTS.DOCUMENT_STATUS_ID,'''') DOCUMENT_STATUS_ID,ISNULL(DOCUMENTS_STATUS.DOCUMENT_STATUS_DESCRIPTION,'''') DOCUMENT_STATUS_DESCRIPTION, DOC_ID AS PATIENT_DOCUMENT_ID,
ISNULL(NULL,''0'') AS ISDICOM, DOCUMENTS.PATIENT_ACCOUNT, ISNULL(NULL,0) AS DOCUMENT_INDEX,ISNULL(DOCUMENTS.ASSIGNED_TO,'''') ASSIGNED_TO, CONVERT(VARCHAR,DOCUMENTS.CONTENT_START_DATE,101) AS CONTENT_START_DATE,
ISNULL(DOCUMENTS.NOTES,'''') AS COMMENTS, '''' AS CHART_ID, UPPER(PM.LAST_NAME)LAST_NAME,UPPER(PM.FIRST_NAME) FIRST_NAME,
ISNULL(CONVERT(VARCHAR,DOCUMENTS.CONTENT_END_DATE,101),'''') AS CONTENT_END_DATE, ISNULL(DOCUMENTS.CREATED_BY,'''') CREATED_BY, ISNULL(DOCUMENTS.CREATED_DATE,'''') AS [CREATED DATE],
ISNULL(DOCUMENTS.MODIFIED_BY,'''') MODIFIED_BY, ISNULL(DOCUMENTS.MODIFIED_DATE,'''') MODIFIED_DATE, DOCUMENTS.DELETED, '''' AS SOURCE_PATH, ISNULL(DOCUMENTS.CONFIDENTIAL,0) CONFIDENTIAL, '''' AS DOC_UPLOAD_NAME, '''' AS DOC_UPLOAD_STATUS,
CONVERT(BIT,''TRUE'') IS_MISC_DOC,'''' AS BUTTON , DOCUMENTS.SIGNED AS SIGNED, DOCUMENTS.SIGN_BY AS SIGNED_BY, DOCUMENTS.SIGN_DATE AS SIGNED_DATE,
PM.PRACTICE_CODE AS PRACTICE_CODE
FROM PATIENT_DOCUMENTS_OTHERS DOCUMENTS
LEFT OUTER JOIN DOCUMENT_CATEGORIES ON DOCUMENTS.CATAGORY_TYPE_ID=DOCUMENT_CATEGORIES.DOCUMENT_CATEGORY_ID
INNER JOIN DOCUMENTS_CATEGORY_MAIN DCM ON DOCUMENTS.DOCUMENTS_CATEGORY_MAIN_ID =DCM.DOCUMENTS_CATEGORY_MAIN_ID
INNER JOIN PATIENT PM ON PM.PATIENT_ACCOUNT=DOCUMENTS.PATIENT_ACCOUNT
LEFT OUTER JOIN DOCUMENTS_STATUS ON DOCUMENTS_STATUS.DOCUMENT_STATUS_ID=DOCUMENTS.DOCUMENT_STATUS_ID
left outer join PATIENT_DOCUMENTS on DOCUMENTS.Doc_Id=PATIENT_DOCUMENTS.PATIENT_DOCUMENT_ID
WHERE ISNULL(DOCUMENTS.DELETED,0)<>1
AND PM.Patient_GlobalId='''+ #GLOBALPATIENTACCOUNT + ''' AND PM.PRACTICE_CODE<>''' + #practice_code + ''' and DOCUMENTS.confidential<>1'
IF(#dateFrom <> 'null')
BEGIN
SET #sqlstr = #sqlstr + 'and ISNULL(CONVERT(VARCHAR,PATIENT_DOCUMENTS.CREATED_DTAE,101),'''') >= ISNULL(CONVERT(VARCHAR,''' + #dateFrom + ''',101),'''') and ISNULL(CONVERT(VARCHAR,PATIENT_DOCUMENTS.CREATED_DTAE,101),'''')<=ISNULL(CONVERT(VARCHAR,''' + #dateTo + ''',101),'''')'
END
IF (#startFrom <> 'null')
BEGIN
SET #sqlstr = #sqlstr + 'CONVERT(VARCHAR,PATIENT_DOCUMENTS.CONTENT_START_DATE,101) >= CONVERT(VARCHAR,''' + #startFrom + ''',101) and CONVERT(VARCHAR,PATIENT_DOCUMENTS.CONTENT_START_DATE,101)<=CONVERT(VARCHAR,''' + #startTo + ''',101)'
END
SET #sqlstr = #sqlstr + ') PatDocTable order by PRACTICE_CODE,CREATED_DTAE desc '
EXECUTE sp_executesql #statement = #sqlstr
PRINT #sqlstr
It gives me error Procedure expects parameter '#statement' of type 'ntext/nchar/nvarchar',but when i change datatype #sqlstr AS nvarchar (max),error omitted,but it trancuate my query.Kindly help me to figure it out?
sp_executesql takes NVARCHAR as parameter not VARCHAR, change varchar(max) to nvarchar(max) will fix the problem.
DECLARE #sqlstr AS nvarchar (max)
Here is one of the possible solution:
Convert your #sqlstr to varchar(max) and instead of EXECUTE sp_executesql, use EXECUTE(#strsql).In this fashion you got your complete print query and it will also execute your query.Hope that it helps.
Declare variable as a nvarchar type when you are using command exec sp_executesql.Above you used varchar for #sqlstr variable.
Eg.
Declare #Sqlstr nvarchar(max)
SET #Sqlstr='...Your dynamic query...'
exec sp_executesql #Sqlstr
Note:Dont use brackets in exec command
using sp_executesql is very good option for this type of queries and it is not very friendly for sql injection.
ex:
DECLARE #sqlString nvarchar(500);
DECLARE #paramDefinition nvarchar(500);
DECLARE #sid int = 12546;
SET #sqlString = 'select * from SameTableBase S Where S.Id = #id';
SET #paramDefinition = N'#id int';
EXECUTE sp_executesql #sqlString , #paramDefinition , #id = #sid
I am trying to insert ID values in stored procedure from .net and the int value for ID is inserting negative value in the stored procedure.
But when a negative value is passed its giving me an error incorrect syntax near '*'.
Please help me.
Here is my stored procedure
ALTER PROCEDURE [dbo].[HotlinePlusAdministration_ArticleMigrator]
#Id AS INT,
--#CategoryID AS INT,
--#Title AS Varchar(200),
--#ArticleDate AS datetime,
#DestLinkServer AS VARCHAR(50),
#UserID AS VARCHAR(8),
#ReturnMsg AS VARCHAR(1000) OUTPUT
AS
BEGIN
DECLARE #Query AS NVARCHAR(4000)
DECLARE #Log AS VARCHAR(8000)
DECLARE #ArticleID as int
DECLARE #NewArticleID as int
DECLARE #ArticleKeyExists as int
DECLARE #Title as varchar(200)
DECLARE #CategoryID as INT
DECLARE #ArticleDate as varchar(30)
DECLARE #ParmDefinition nvarchar(500);
SET XACT_ABORT ON -- Required for nested transaction
BEGIN TRAN
-- Check if ArticleID exists in Destination Server
SET #Query = N' SELECT #ArticleKeyExists = COUNT(*)
FROM ' + #DestLinkServer + '.HL2_61.dbo.Article' + ' where ArticleKey = ' + str(#Id)
SET #ParmDefinition = N' #ID int, #ArticleKeyExists int OUTPUT';
EXECUTE sp_executesql #Query , #ParmDefinition, #ID, #ArticleKeyExists OUTPUT;
--EXECUTE sp_executesql 1234,'BRHLSQL8','BRWSQLDC',#return = retnmsg
IF ##ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
SET #ReturnMsg = #Log + '<span style="color:red;">ERROR: <br></span>'
RETURN -1
END
--Delete existing Articles for select page
set #Query = 'DELETE FROM ' + #DestLinkServer +
'.HL2_61.dbo.Article ' +
'WHERE ArticleKey = ' + CONVERT(VARCHAR, #Id)
--'WHERE CategoryID = ' + CONVERT(VARCHAR, #CategoryID) + ' and Title = ''' + #Title + ''' and ArticleDate = ''' + #ArticleDate + ''''
Print #Query
EXEC(#Query)
When I am executing the code as below I am getting the error.
DECLARE #return_value int,
#ReturnMsg varchar(1000)
EXEC #return_value = [dbo].[Migrator]
#Id = -1591276581,
#DestLinkServer = N'SQLDC',
#UserID = N'10c1',
#ReturnMsg = #ReturnMsg OUTPUT
SELECT #ReturnMsg as N'#ReturnMsg'
SELECT 'Return Value' = #return_value
GO
Please someone help me..
Thanks
When you convert an int to a varchar, you have to specify the size:
Try this:
CONVERT(VARCHAR(50), #Id)
and avoid using str(#Id)
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!