6

Ok here is my problem :

Before i start the description, let me to tell you that I have googled up a lot and I am posting this question for a good optimal solution :)

i am building a rest service on WCF to get userProfiles... the user can filter userProfiles by giving something like userProfiles?location=London

now i have the following method

GetUserProfiles(string firstname, string lastname, string age, string location)

the sql query string i built is: select firstname, lastname, .... from profiles where (firstName like '%{firstname}%') AND (lastName like '%{lastName}%') ....and so on with all variables being replaced by string formatter.

Problem with this is that it filters any row having firstname, lastname, age or location having a null value....

doing something like (firstName like '%{firstName}%' OR firstName IS NULL) would be tedious and the statement would become unmaintanable! (in this example there are only 4 arguments, but in my actual method there are 10)

What would be the best solution for this?....How is this situation usually handled?

Database used : MySql

2
  • Have you considered using LINQ-to-SQL? Commented Mar 12, 2010 at 8:02
  • no i have not yet considered linq to sql... Commented Mar 12, 2010 at 8:22

4 Answers 4

2

It's a fundamental property of NULL that when you compare it to anything—including NULL—it returns false, which is why what you're doing doesn't work.

So the first question to answer is: why do you want a row with a NULL lastname be returned when they enter a lastname of "smith"? Possibly you mean that either the firstname or the lastname needs to match to be returned, in which case you're not running the correct query. The most naive solution is:

SELECT firstname, lastname, ....
FROM profiles
WHERE IFNULL(firstName LIKE'%{firstname}%'
OR lastName LIKE '%{lastName}%'

Now this will work for several hundreds and possibly several thousand rows but won't scale beyond that for several reasons:

  1. ORs are typically woeful in terms of performance. Avoid them where possible. If you look at database applications written by experienced programmers you probably won't find a single OR condition (except from those who proselytize the virtues of OR on the back of having once written a guestbook application that has 3 users and 2 hits a month);
  2. Doing a LIKE with a % at the front will mean no indexes will be sued.

There are several solutions to (2). Probably the easiest in MySQL is to use full text searching on MyISAM tables. It won't find matches like "Johannes" if you type in "han" but it is generally sufficient.

Often you deal with OR conditions by using either UNION or (preferably) UNION ALL on multiple queries. For example:

SELECT firstname, lastname, ....
FROM profiles
WHERE firstName LIKE'%{firstname}%'
AND lastName LIKE '%{lastName}%'
UNION ALL
SELECT firstname, lastname, ....
FROM profiles
WHERE firstName LIKE'%{firstname}%'
AND lastname IS NULL
UNION ALL
SELECT firstname, lastname, ....
FROM profiles
WHERE firstName IS NULL
AND lastName LIKE '%{lastName}%'
UNION ALL
SELECT firstname, lastname, ....
FROM profiles
WHERE firstName IS NULL
AND lastName IS NULL

Looks big and ugly but UNION ALL scales extremely well (ignoring the % at the start of the LIKE criteria). It's basically concatenating (in this case) four queries. A UNION will do an implicit DISTINCT on the result rows but we know there'll be no overlap here because we're varying checking for NULL.

Another possibility is not to treat NULL as something you want to search for. This is a far better and more intuitive solution (imho). If someone types in a lastname of "Smith" do you really want rows with a NULL lastname showing up?

Or do you want them to show up because you might've got a match on the first name? If so, you want a slightly different query. For example:

SELECT firstname, lastname, ....
FROM profiles
WHERE id IN (
  SELECT id 
  FROM profiles
  WHERE firstName LIKE'%{firstname}%'
  UNION ALL
  SELECT id 
  FROM profiles
  WHERE lastName LIKE '%{lastName}%'
)
Sign up to request clarification or add additional context in comments.

1 Comment

@glenn: like I said, the four union variant is going to be mostly pointless in this case because the criteria won't use indexes anyway but if you were using indexes then that's a different story. The bigger issue is whether you're running the right query and whether your data model is suboptimal or not.
1

You can use COALESCE(value,...)

coalesce(firstName, '') like '%{firstname}%'

1 Comment

COALESCE() is basically an O(n) lookup as no indexes will be used.
0

You can use COALESCE:

select firstname, lastname, ....
from profiles
where (coalesce(firstName, '') like '%{firstname}%')
AND (coalesce(lastName, '') like '%{lastName}%')

2 Comments

COALESCE() is basically an O(n) lookup as no indexes will be used.
Yes, obviously, but you can't use indexes anyway because of the LIKE '%foo%' so it seems to be a moot point here. The index can be used with `LIKE 'foo%' but not if there's a wildcard at the start. If performance is an issue here, a full text index should be used.
0

You could always disallow null values in the first place - after all, everybody must be alive for some positive number of years and be somewhere geographically.

If you absolutely must allow nulls, then either use COALESCE, as others have suggested, or possibly IFNULL which is MySQL specific and may be slightly more optimal since it takes exactly 2 paramters.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.