views:

210

answers:

6

Hello,

I am passing a comma-delimited list of values into a stored procedure. I need to execute a query to see if the ID of an entity is in the comma-delimited list. Unfortunately, I think I do not understand something.

When I execute the following stored procedure:

exec dbo.myStoredProcedure @myFilter=N'1, 2, 3, 4'

I receive the following error:

"Conversion failed when converting the varchar value '1, 2, 3, 4' to data type int."

My stored procedure is fairly basic. It looks like this:

CREATE PROCEDURE [dbo].[myStoredProcedure]
    @myFilter nvarchar(512) = NULL
AS
SET NOCOUNT ON
BEGIN
    -- Remove the quote marks so the filter will work with the "IN" statement
    SELECT @myFilter = REPLACE(@myFilter, '''', '')

    -- Execute the query
    SELECT
     t.ID,
     t.Name  
    FROM
     MyTable t
    WHERE
     t.ID IN (@myFilter)
    ORDER BY
     t.Name
END

How do I use a parameter in a SQL statement as described above? Thank you!

+3  A: 

You need to split the string and dump it into a temp table. Then you join against the temp table.

There are many examples of this, here is one at random.

http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/01/t-sql-split-function.aspx

Jonathan Allen
Alternately, you can convert the comma-delimited list into XML and pass the XML into the stored procedure as an argument. You'd still be joining against the XML, but you'd at least avoid writing a split function.
iKnowKungFoo
@iKnowFungFoo: Using XML means eating up characters with markup rather than content - runs the risk of not being able to send all content
OMG Ponies
@iKnowKungFoo: On top of what @rexem said, if you have many data to send, markup becomes too big and thus the query slows down.
Sung Meister
A: 

I would create a function that takes your comma delimited string and splits it and returns a single column table variable with each value in its own row. Select that column from the returned table in your IN statement.

jlech
A: 

I found a cute way of doing this - but it smells a bit.

declare @delimitedlist varchar(8000)
set @delimitedlist = '|1|2|33|11|3134|'

select * from mytable where @delimitedlist like '%|' + cast(id as varchar) + '|%'

So... this will return all records with an id equal to 1, 2, 33, 11, or 3134.

EDIT: I would also add that this is not vulnerable to SQL injection (whereas dynamic SQL relies on your whitelisting/blacklisting techniques to ensure it isn't vulnerable). It might have a performance hit on large sets of data, but it works and it's secure.

Mayo
I think you would prevent SQL from using an index seek and force it to scan,
Andrew
cast varchar as ??? should at least define more than a sigle char.
astander
@Andrew: you're correct - it would perform miserably because it can't use an index due to using % at the start of the LIKE criteria.
OMG Ponies
Mayo
sql server 2005 has MAX, read about it?
astander
no need for the snark, astander.
Peter
Facts or snarks?
astander
I think we are having a dog-shed moment.
Peter
k, its fine we know the answer...
astander
I meant, I think we are having a bike-shed moment.
Peter
OK, then you peddle
astander
Declaring varchar without length is a horrible habit, sorry. In some cases it will truncate to 30 characters, in others it will truncate to 1. Oh, and it won't tell you that it's truncated; nothing like silent data loss to start off your week. This isn't something to be brushed off, IMHO. Using it *never* will prevent you from using it in the wrong place without realizing it. http://is.gd/4wsl3
Aaron Bertrand
The op also forgot to explicitly list columns and he didn't specify the table owner / schema. His nomenclature for variable/column/table names is lacking. He also should break up the statement into multiple lines for readability. I'm ashamed to share the same name.
Mayo
+2  A: 

Absent a split function, something like this:

CREATE PROCEDURE [dbo].[myStoredProcedure]
    @myFilter varchar(512) = NULL -- don't use NVARCHAR for a list of INTs
AS
SET NOCOUNT ON
BEGIN
    SELECT
        t.ID,
        t.Name          
    FROM
        MyTable t
    WHERE
        CHARINDEX(','+CONVERT(VARCHAR,t.ID)+',',@myFilter) > 0
    ORDER BY
        t.Name
END

Performance will be poor. A table scan every time. Better to use a split function. See: http://www.sommarskog.se/arrays-in-sql.html

Peter
+4  A: 

You could make function that takes your parameter, slipts it and returns table with all the numbers in it.

If your are working with lists or arrays in SQL Server, I recommend that you read Erland Sommarskogs wonderful stuff:

Arrays and Lists in SQL Server 2005

pirho
A: 

I have a couple of blog posts on this as well, with a lot of interesting followup comments and dialog:

http://is.gd/4wsnn

http://is.gd/4wsp3

Aaron Bertrand