views:

490

answers:

4

Hi, I've the following sql query:

SQL = "SELECT * FROM Product WHERE ProductCategoryId = " & Request.QueryString("CategoryId")

This query tells to get all the products from ONE category. I want the ability so products can be from some categories, and not from one category only.

So, I changed the [Product].ProductCategoryId field to varchar(50), and wrriten some categories id separated in commas, for example:

1,5,7,4

Now, how can I get this working in the SQL query? In ASP there are the Split and Contains (http://www.devx.com/vb2themax/Tip/18364) functions, but how can I do this in SQL?

Thanks, and sorry for English...

+1  A: 

http://www.sommarskog.se/arrays-in-sql.html

HLGEM
+2  A: 

Not for nothing, but WHERE ProductCategoryId = " & Request.QueryString("CategoryId") is a GOD AWFUL way of doing your query.

You've just opened yourself up to crazy SQL injection attacks.

Jack Marchetti
+3  A: 

Obligatory note: NEVER concatenate user-supplied data into a SQL query; you are opening yourself up to SQL injection attacks.

My previous answer (using "IN") was based on a mistaken idea that you were collecting multiple IDs from the user, and searching against a column with one value. But you're doing the opposite -- the column has multiple values, and you're trying to match against one value coming from the user.

This is a bad way to do it. Don't use comma-separated values; add a new table with a one-to-many relationship. Then the query will look like

SQL = "SELECT Product.* FROM Product, ProductCategory WHERE ProductCategory.ProductId=Product.ID AND ProductCategory.CategoryId = " & Request.QueryString("CategoryId")

If you really really want to do the comma-separated thing, you could do a search using LIKE and % wildcards, but it will be fragile (e.g. you'll have to make sure that an ID of "2" doesn't match "12").

JacobM
What's that xkcd story again? ;-)
Arthur Reutenauer
Obviously we would want him to use parameterized queries/sprocs, however I don't think you can pass in a parameter of let's say @var = "1,2,3,4" and then do an IN on it.
Jack Marchetti
Ah, that <a href="xkcd.com/327/">here</a>.
Arthur Reutenauer
Thanks but, I get this error: Conversion failed when converting the varchar value '1,2' to data type int.I printed the sql query, and the result is: SELECT * FROM Product WHERE ProductCategoryID IN (2)BTW: I don't care from SQL Injection attacks! It's a local project.
TTT
Any ideas how to solve this?
TTT
Wait, I misunderstood what you did -- the field in the database is the one with the commas in it. I'll add a new answer.
JacobM
+1  A: 

Looking past all injection issues, this is the best source for TSQL list manipulation. Without reading the entire article, here is what is needed to create a very fast loop free TSQL method to split strings.

You will end up with a TSQL split function, and can use it like:

SELECT
    y.*
    FROM YourTable y
        INNER JOIN dbo.FN_ListToTable(',','1,2,3,444,5,,,6') s ON y.ID=s.ListValue

from the previous article, I prefer the number table approach and this is what you need to do to implement it.

For this method to work, you need to do this one time table setup:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

Once the Numbers table is set up, create this function:

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(   ----------------
    --SINGLE QUERY-- --this will not return empty rows
    ----------------
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''
);
GO

You can now easily split a CSV string into a table and join on it:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,')

OUTPUT:

ListValue
-----------------------
1
2
3
4
5
6777

(6 row(s) affected)

Your can pass in a CSV string into a procedure and process only rows for the given IDs, or just use it in the query like:

SELECT
    y.*
    FROM YourTable y
        INNER JOIN dbo.FN_ListToTable(',',@GivenCSV) s ON y.ID=s.ListValue
KM