Problem with IN Statement in SQL Server

I currently have a a query where I pass in some delimited values as a varchar. When I go to test against this list in my Where clause SQL Server freaks out and tells me I need to have integers . Not sure what to do.

@states varchar(max)
in C# I pass this in as - @states='AL,CT,FL'

Then in my query it looks like this :

Select *
From dhpDrivers
Where  dhpDriver.State in(@states)


error I get is :
Conversion failed when converting the varchar value 'AL,CT,FL' to data type int

The dhpDriver.State  field is not an integer field so I'm a bit confused.Any idea what I need to do with my states list to make this work ? Any advice would be much appreciated.

Jason
jazzcatoneAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Paul MacDonaldDirector, Information SystemsCommented:
Is this a stored procedure?  Is the passed parameter defined as an Int?
Kent OlsenDBACommented:
Hi Jazz,

You've got several problems here, the biggest is "You can't do that".

Select *
From dhpDrivers
Where  dhpDriver.State in(@states)

You can't pass a variable to IN.  Even though it looks like a function, IN is an operator and it requires a parameter list, not a string.

You can create a string with the entire query and execute it, but that may no help this particular issue.


Also, the error that you're seeing is a conversion to integer error.  That implies that dhpDriver.State is an integer (as in the logical state of the driver) not a string (as in a state in the U.S.A.)


So the first issue is to determine what you need to compare and the source field that contains the value.


Good Luck,
Kent

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Christopher GordonSenior Developer AnalystCommented:
Try using CHARINDEX.

For Example...


declare @myFilter nvarchar(100) = 'AL,CT,FL'
declare @myTable table (id int identity(1,1), myState nvarchar(100))

insert into @myTable
values ('OH'),
 ('NY'),
 ('MI'),
 ('CT'),
  ('FL'),
 ('AL')
 
 
select      *
from      @myTable
where      CHARINDEX(myState, @myFilter) > 0
SolarWinds® Network Configuration Manager (NCM)

SolarWinds® Network Configuration Manager brings structure and peace of mind to configuration management. Bulk config deployment, automatic backups, change detection, vulnerability assessments, and config change templates reduce the time needed for repetitive tasks.

jazzcatoneAuthor Commented:
dhpDriver.State is a char(2). This is source field I need to compare against.
Scott PletcherSenior DBACommented:
Looks like the parameter type of @states is an int.  Change the param to varchar(nnnn).

Best would likely be convert the list passed into a table, since that can be keyed.

DECLARE @statesTable TABLE (
    state char(2) PRIMARY KEY
    )

-- code to insert values from @states to @statesTable

SELECT *
FROM dbo.dhpDrivers d
INNER JOIN @statesTable st ON
    st.state = d.state
Scott PletcherSenior DBACommented:
Here is the:
-- code to insert values from @states to @statesTable


-- insert the first state into the table
INSERT INTO @statesTable
SELECT LEFT(@states, CHARINDEX(',', @states) - 1)

-- dynamically create a table of seq #s from 1 to 9999 to help parse input parameter
;WITH
cteDigits AS (
    SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
    SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
),
cteTally AS (
    SELECT [1000s].digit * 1000 + [100s].digit * 100 + [10s].digit * 10 + [1s].digit AS num
    FROM cteDigits [1s]
    CROSS JOIN cteDigits [10s]
    CROSS JOIN cteDigits [100s]
    CROSS JOIN cteDigits [1000s]    
)
INSERT INTO @statesTable
SELECT
    SUBSTRING(@states, num + 1, CHARINDEX(',', @states + ',', num + 1) - num - 1) AS state
FROM cteTally
WHERE
    num BETWEEN 2 AND LEN(@states) AND
    SUBSTRING(@states, num, 1) = ','
HainKurtSr. System AnalystCommented:
use this query and you are done

Select *
From dhpDrivers
Where ','+@states+',' like '%,'+State+',%'

simple is the best...
Scott PletcherSenior DBACommented:
Not necessarily for performance it isn't.

That could have awful performance, depending on the number of rows and number of values entered, and if a state index exists on the table.
HainKurtSr. System AnalystCommented:
ok! if we have a look-up table for states we can use this format :) and still use index and performance would not be awful :)

Select d.*
From dhpDrivers d inner join dphStates s on s.state=d.state and ','+@states+',' like '%,'+s.State+',%'
Scott PletcherSenior DBACommented:
If you could create a table that's guaranteed to hold all the state values (*), you could do this:


SELECT ...
FROM dbo.dhpDrivers d
INNER JOIN (
    SELECT state
    FROM dbo.dphStates
    WHERE
        ',' + @states + ',' LIKE '%,' + state + ',%'
) AS s ON
    s.state = d.state
...


(*) Sometimes "state" includes things like "DC", Canadian provinces, etc., so it's more than just a fixed list of (exactly) 50 values.

I still see that as a disadvantage over just dynamically creating a much more limited table from only the state values that are entered in the parameter for that run.


However, I'd probably change to a true temporary table, #states, rather than @statesTable, so that stats are available for SQL, so the optimizer will "know" it's a very small table.
HainKurtSr. System AnalystCommented:
ScottPletcher, is your last post any different then my post @ 37744667 :)
Scott PletcherSenior DBACommented:
Yes.  I reduced the number of rows as much as possible BEFORE joining to the main table.

Again, I still think that is an inferior approach to creating a temp table of the actual values passed in, since very few values are likely to be passed in for a typical run.
jazzcatoneAuthor Commented:
So I created a User Defined function called CSVStringToTable to pump out my @states into a single column table.

So when @states = 'AL,OH,PA'
and I call the function like so : CSVStringToTable(@states)

I get a single column table (column name is ItemList) with 3 records. One record per state.

so in my stored proc I create a temp table like so :
SELECT * INTO #TEMP_StateList34 FROM CSVStringToTable(@states)

this lets lets me do my in function from my where clause:
WHERE dhpDriver.State in(SELECT Itemlist from #TEMP_StateList34)

AS Kdo mentioned earlier the problem is that you really need something that sql server sees as an list and not a string. Thank you for all the help.
Scott PletcherSenior DBACommented:
Looks good.

A primary key on the temp table should help SQL perform (slightly) better.  The more states there are in the table, the more they key should help.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft SQL Server 2005

From novice to tech pro — start learning today.