Using the IN Clause With Stored Procedures 

Recently I have been working on creating a dynamic reporting engine for use within the DotNetNuke system, a method to allow for the execution of a stored procedure and the proper display of the results. This includes creating a list of parameters, including dynamically loaded lookup systems and more. Well the most complex item that I have had to condition for was how to deal with filter options that require the use of the SQL IN clause, there are many ways of doing this, but I have found a way that appears to work perfect for my needs, this will be explained in this posting.

Disclaimer

This solution is provided as an example, and may not work 100% to meet your specific business needs, careful consideration must be taken when working with the code provided below as due to its dynamic nature SQL injection items need to be considered when investigating the input data provided to the stored procedure. If you have questions about the limitations or security surrounding this sample, please e-mail me or make a post to the forums on this site.

Pre-Requisites

This example assumes that you already have a comma separated string that contains integer values that should be used as part of a list to be used inside of an "IN" clause. For example if you have a query that looks like the following.

SELECT *
FROM Reports
WHERE ReportId IN (@Reports)

This will NOT work in a stored procedure. The most common solution is to use dynamic SQL, which I personally do not like using. My solution will modify this to use a custom created Function that will parse the values into a table, then use that table to get the listing.

 

The Solution

As I mentioned above, the solution is very simple, create a function that converts the data into a table. Below is the needed procedure, please see the rest of the article for important notes.

CREATE FUNCTION dbo.funcListToTableInt(@list as varchar(8000), @delim as varchar(10))
RETURNS @listTable 
table(
  Value 
INT
  
)
AS
BEGIN
    
--Declare helper to identify the position of the delim
    
DECLARE @DelimPosition INT
    
    
--Prime the loop, with an initial check for the delim
    
SET @DelimPosition = CHARINDEX(@delim, @list)

    
--Loop through, until we no longer find the delimiter
    
WHILE @DelimPosition > 0
    
BEGIN
        
--Add the item to the table
        
INSERT INTO @listTable(Value)
            
VALUES(CAST(RTRIM(LEFT(@list, @DelimPosition - 1)) AS INT))
    
        
--Remove the entry from the List
        
SET @list = right(@list, len(@list) - @DelimPosition)

        
--Perform position comparison
        
SET @DelimPosition = CHARINDEX(@delim, @list)
    
END

    
--If we still have an entry, add it to the list
    
IF len(@list) > 0
        
insert into @listTable(Value)
        
values(CAST(RTRIM(@list) AS INT))

  
RETURN
END
GO

The above code creates a function that will process our input, converting each value to an integer and returning a single column table. The procedure created is called, funcListToTableInt. Now, with a simple modification to the sample query we are able to successfully run the query as part of a stored procedure.

SELECT *
FROM Reports
WHERE ReportId IN (
		    SELECT Value
		    FROM funcListToTableInt(@Reports,',')
                   )

 

Conclusion

This method provides a simple manner to convert a comma separated listing of values to something that can be used in an IN clause within SQL Server Stored procedures. Two parameters are passed to the function, allowing other delimiters to be used if needed. One could also use this for IN's of different datatypes, but a new function would be needed. I hope that this has helped someone solve a dynamic query situation. As always please provide any feedback below and any questions to the forum.

Posted by Mitchel on Friday, August 08, 2008
 

Comments

Very incite full, thanks for the blog post. I was just pondering how this may be accomplished.

By Bruce de Beer on Tuesday, August 12, 2008 at 8:05 PM

I have been trying to figure this out for some time, THANK YOU!

By Jeff on Wednesday, October 15, 2008 at 10:00 AM

As a followup, how could the WHERE clause be designed to handle both a comma delimited list or ALL items? I am passing the variable "ALL" or the list into the stored procedure.

By Jeff Baisch on Wednesday, October 15, 2008 at 4:10 PM

Typically I would use a conditional to use one of two forms of the query.

Make a post in the forum if you want a detailed sample.

By mitchel.sellers@gmail.com on Wednesday, October 15, 2008 at 4:17 PM

Hi,

I tried your soution as per this sp:

CREATE PROCEDURE [dbo].[usp_Audit_Create_New]

(
@AssetTypeID int,
@AssetIn int
)

AS

INSERT INTO tblAudit
(AssID, BarCode, SerialNo, ISSINo, AssTypeID, AssetType, Description, ToUserID, ToUserName, OpID, OperationName, AuthID, AuthorityNo, EMail)
SELECT tblAsset.AssID, tblAsset.BarCode, tblAsset.SerialNo, tblAsset.ISSINo, tblAsset.AssTypeID, tblAsset.AssetType, tblAsset.Description, tblAsset.ToUserID,
tblAsset.ToUserName, tblAsset.OpID, tblAsset.OperationName, tblAsset.AuthID, tblAsset.AuthorityNo, tblUsers.EMail
FROM tblAsset INNER JOIN
tblUsers ON tblAsset.ToUserID = tblUsers.UserID
--WHERE (tblAsset.CurrentState = 1) AND (tblAsset.AssTypeID = @AssetTypeID) AND (tblAsset.AssID IN (@AssetIn))
WHERE (tblAsset.CurrentState = 1) AND (tblAsset.AssTypeID = @AssetTypeID) AND (tblAsset.AssID IN (SELECT Value FROM funcListToTableInt(@AssetIn,','))


-- Declare a variable
DECLARE @AuditNo int

--Find the last audit number
SELECT @AuditNo = Max(AuditNo) FROM tblAudit

--Update the audit table with a a new audit number
UPDATE tblAudit SET AuditNo = @AuditNo+1 WHERE AuditNo = 0
GO

The SQL statement therefore read as an example (executed in classic ASP):

EXEC dbo.usp_Audit_Create_New 9, 1, 9, 10, 11, 12

The first 9 being the asset type and the remainder being the IN clause string from a page using check boxes as a select procedure for the records to be included.

I still got the following back as I had done when I tried it without the function:

Procedure or function usp_Audit_Create_New has too many arguments specified..

Thanks for any help or advice you can give, I am using a full sql statement in the page at the moment to solve this but would prefer an sp which did the job if possible.

Steve Medcalf

By Steve Medcalf on Sunday, June 21, 2009 at 4:44 AM

Beautiful solution..!

By Nicolás on Thursday, July 09, 2009 at 12:07 PM

You save my day!!!!!!
I really appreciate this post, thanks a lot ^ ^

By Alan on Thursday, August 27, 2009 at 2:46 AM

Just wanted to say thanks for this post, it really helped me out.

By James on Wednesday, July 07, 2010 at 7:48 AM

Mitchel, is it OK to use your function in a project I'm working on? I suppose I could take the basic idea and write my own version of the function, but would rather not bother.

By Vince P. on Thursday, September 09, 2010 at 4:01 PM

Oh... and thanks for posting this. It's a great example of table functions, though it would be nice if one could just use variables with IN clauses instead.

By Vince P. on Thursday, September 09, 2010 at 4:03 PM

You can use it for any purpose you see fit!

By mitchel.sellers@gmail.com on Thursday, September 09, 2010 at 4:10 PM

Ah.. thanks! Turns out we had a similar one on the server already which I didn't know about. :)

It takes a text parameter instead of varchar. Because charindex() doesn't work with text fields, it works through the input parameter in chunks using substring() to get the next chunk, then using charindex() on that.

I know this thread is old for you now, but just thought you would find that mildly interesting anyway.

Cheers!

By Vince P. on Thursday, September 09, 2010 at 4:32 PM

Can we create session in stored procedure?
I want to use session in where clause?
please reply.....

By Minal Vithalani on Thursday, August 18, 2011 at 1:34 AM

Someone asked "As a followup, how could the WHERE clause be designed to handle both a comma delimited list or ALL items? I am passing the variable "ALL" or the list into the stored procedure. "

I'm doing this as follows:

WHERE CASE WHEN ISNULL(@MyList,'') = '' THEN
1
ELSE
(select count(*) FROM dbo.fnListToTableInt(@MyList,',') WHERE [Value] = MyTable.FieldIWantToCheck)
END >= 1

If the value list is null or empty, it returns 1 , otherwise it returns how many matching values were returned from the function. Then it checks for that to be >=1.

By Eric G on Tuesday, November 15, 2011 at 3:47 PM

Good one. Thanks

By Vaithi on Tuesday, November 22, 2011 at 5:30 AM

Here is another solution to do it without dynamic query. It can be done with the help of xquery as well.
SET @Xml = cast((''+replace('15,16,17',',' ,'')+'') AS XML)
Select @Xml

SELECT A.value('.', 'varchar(max)') as [Column] FROM @Xml.nodes('A') AS FN(A)

Here is the complete solution : http://raresql.com/2011/12/21/how-to-use-multiple-values-for-in-clause-using-same-parameter-sql-server/

By Imran on Wednesday, December 21, 2011 at 2:30 PM

Name (required)

Email (required)

Website

CAPTCHA image
Enter the code shown above:

Content provided in this blog is provided "AS-IS" and the information should be used at your own discretion.  The thoughts and opinions expressed are the personal thoughts of Mitchel Sellers and do not reflect the opinions of his employer.

Friend of RedGate

www.datasprings.com - DotNetNuke ModulesICG

Click here for advertising information.

Content in this blog is copyright protected.  Re-publishing on other websites is allowed as long as proper credit and backlink to the article is provided.  Any other re-publishing or distribution of this content is prohibited without written permission from Mitchel Sellers.