views:

58

answers:

4

Sorry for the long post, but most of it is code spelling out my scenario:

I'm trying to execute a dynamic query (hopefully through a stored proceedure) to retrieve results based on a variable number of inputs.

If I had a table:

(dbo).(People)
ID   Name   Age
1    Joe    28
2    Bob    32
3    Alan   26
4    Joe    27

I want to allow the user to search by any of the three columns, no problem:

DECLARE @ID int, @Name nvarchar(25), @Age int
SET @ID = 1
SET @Name = 'Joe'
SET @Age = null

SELECT *
FROM dbo.People
WHERE
(ID = @ID or @ID is null) AND
(Name like @Name or @Name is null) AND
(Age = @Age or @Age is null)

And I retrieve the result that I want.

Now, if I want to search for multiple fields in a column, I can do that no problem:

DECLARE @text nvarchar(100)
SET @text = '1, 3'

DECLARE @ids AS TABLE (n int NOT NULL PRIMARY KEY)

--//parse the string into a table
DECLARE @TempString nvarchar(300), @Pos int
SET @text = LTRIM(RTRIM(@text))+ ','
SET @Pos = CHARINDEX(',', @text, 1)
IF REPLACE(@text, ',', '') <> ''
BEGIN
    WHILE @Pos > 0
    BEGIN
        SET @TempString = LTRIM(RTRIM(LEFT(@text, @Pos - 1)))
        IF @TempString <> '' --just: IF @TempString != ''
        BEGIN
            INSERT INTO @ids VALUES (@TempString)
        END
        SET @text = RIGHT(@text, LEN(@text) - @Pos)
        SET @Pos = CHARINDEX(',', @text, 1)
    END
END


SELECT *
FROM   dbo.People
WHERE  
ID IN (SELECT n FROM @ids)

Now, my issue is I can't seem to figure out how to combine the two since I can't put:

WHERE
(Name like @Name or @Name is null) AND
(Id IN (SELECT n FROM @ids) or @ids is null)

Because @ids will never be null (since it's a table)

Any help would be greatly appreciated!

Thanks in advance...and let me know if I can clarify anything

A: 

You can try:

NOT EXISTS (SELECT 1 FROM @ids)
OR EXISTS (SELECT 1 FROM @ids where n = Id) 

But these better be small tables - this query will probably not play very well with any indexes on your tables.

Steve Broberg
+1  A: 

You could use an IF statement:

IF LEN(@ids) > 0
BEGIN

  SELECT *
    FROM dbo.People
   WHERE ID IN (SELECT n FROM @ids)

END
ELSE
BEGIN

  SELECT *
    FROM dbo.People

END

Otherwise, consider making the query real dynamic SQL (minding pitfalls of course).

OMG Ponies
What if all three of my variables, ID, Name, and Age can each have a list (potentially). That means that I'd have to have 9 different conditions...
Brett
@Brett: Anything beyond one condition leads me to use dynamic SQL. You can do it in a single query, but it will be non-sargable (not perform ideally - see the link in my comment to Cade Roux).
OMG Ponies
@OMG Ponies - Thanks for your help, and I plan on reading more of that document tomorrow, I didn't get to finish it today.... again appreciate all of your help!
Brett
@OMG Ponies - That is a fantastic and well written article... thanks for the link and I'll be sure to pass this around to some of my co-workers. I appreciate the help!
Brett
A: 

Try:

(Id IN (SELECT n FROM @ids) OR NOT EXISTS (SELECT * FROM @ids))
Cade Roux
IIRC, not [sargable](http://en.wikipedia.org/wiki/Sargable)
OMG Ponies
Thanks for the help, that's the fix I needed. but currently my table is pretty small and in time it will grow, and pretty quickly at that, so I'm wondering how negatively this will impact performance...?
Brett
@Brett - I'm with OMG Ponies on the overall approach. This is simply the shortest and simplest answer in keeping with your approach. I decided not to bring up refactoring, but typically the execution plan for "unified" search like this is not good, and you are better off unrolling the conditionals "manually" so that you get best execution plans for each option. When there are complex combinations, generating code dynamically so that it is exactly what is needed is ultimately most efficient. That's a little more of a challenge in your case because of the lists instead of single parameters.
Cade Roux
@Cade - Thank you for the comment, I appreciate the feedback, and since I'm in the development stage, now is the time to explore my options and pursue the more efficient method. I'm glad I learned this route, but will re-work things for a more efficient query!
Brett
A: 

A quick fix:

(`%,' + Id + ',%' like ',' + @ids + ',' or @ids is null)
and (`%,' + Name + ',%' like ',' + @names + ',' or @names is null)

So if the user passes @ids = 1,2, the first row gives:

`%,1,%' like ',1,2,'

It's a good idea to filter out spaces before and after comma's. :)

Andomar