tags:

views:

228

answers:

4

I have a table like this:

+-------------------+---------------+
| description       | colour        |
+-------------------+---------------+
| Macrame Dress     | Washed Black  |
| Darcelle Gilet    | Dirty Shellac |
| Darcelle Cardigan | Washed Black  |
| Let It Rot Vest   | Optic White   |
| Let It Rot Crew   | Washed Black  |
| Let It Rot Crew   | Optic White   |
| Battalion Short   | Somme         |
| Seine Dress       | Washed Black  |
| Seine Dress       | Cocomotion    |
| Odette V-neck     | Linen Marl    |
+-------------------+---------------+
I want search it for "black dress" and it only return rows 1 & 8. If I type in "black", it will return rows 1, 3, 5 & 8. Dress should return rows 1, 8 & 9.

Can anyone think of a really elegant, beautiful bit of sql that will take any number of search words and return the most concise result set. I can think of some pretty ugly ways to do it, but they upset my karma.

A: 

Ideally, I think you would have two separate search terms, one for description (s1) and one for color (s2).

Then your query becomes:

select * from tbl where description like '%(s1)%' and colour like '%(s2)%'

Substituting in the values for s1 and s2 of course.

A blank s2 would result in %% which should match everything (I think).

To be able to search on multiple terms in either field, you need something like

select * from tbl
    where description + colour like '%(s1)%'
      and description + colour like '%(s2)%'
      and description + colour like '%(s3)%'

This will have to be constructed on the fly, based on the number of words in your search pattern (so "black dress" will have s1 and s2, black will just have s1). The "description + colour" bit is concatenated fields; my SQL is a bit rusty so I don't know how you'd do that exactly but the concept is sound.

paxdiablo
thanks Pax, but I think multiple fields dont fit in with a simple user experience. I think a user should be able to type whatever and the code should work around the user.Your multi-concat solution would work, its what I initially came up with, but I was looking for something more elegant.
No problems, stuart, but you're a hard man to please :-) Since your constraints are heavy (no changes allowed to the tables), that hamstrings all the possible answers. You may have to make a choice between your constraints and your desired elegance, depending on which is more important to you.
paxdiablo
+3  A: 

The only nice solution (karma-wise) I can think of would involve using a FULLTEXT index. This would allow you to make use of queries like this:

SELECT * FROM mytable
WHERE MATCH (description,color) AGAINST ('black dress');

Unfortunately, FULLTEXT indexes require the MyISAM table type, if I recall correctly.

EDIT

I doubt that this is what you are looking for but let me add it for the sake of discussion. You could duplicate your data into a temporary MyISAM table and do you full text search on that:

CREATE TEMPORARY TABLE mytmptable ENGINE=MyIsam SELECT * FROM mytable;
ALTER TABLE mytmptable ADD FULLTEXT KEY (description,color); 
SELECT * FROM mytmptable WHERE MATCH (description,color) AGAINST ('black dress');
cg
unfortunately I cant change the Engine, due to other indexing and transactional constraints, but yes, this would have been my ideal solution.
You know you can set the engine used per table, right? So if you are lucky and don't have transactional constraints on *this specific table* you can make it MyISAM without affecting the other tables.[redacted plug for PostgreSQL]
kquinn
no, this table has to be innoDB, its the main product table for the site and so has to be transactional
A: 

What data type are your columns? If they are VARCHAR or similar, you'd simply do

select 
  * 
from 
  tbl 
where 
  description like '%(s1)%' 
  or colour like '%(s1)%'

(This is adapted from @Pax, who I believe misunderstood the question -- I take it to mean it should match whenever the search term appears in 'description' or 'colour'.)

chryss
this would return results if the colour was like black or description was like dress, but I need to only return black dresses. its an AND, but over 2 columns. I plan to use an OR in a second query as part of a UNION as a fall back
+1  A: 
where colour+description like '%s1%'
Learning
thats the solution I came up with, but I'm concerned that "Washed Black Macrame Dress" would not be a match for "black dress"
then we need to break the two search terms and have :where colour+description like '%s1%'and colour+description like '%s2%'
Learning
.. and of course no indexes will be used and a full table scan will ensue.
Learning