Search code examples
mysqlsqlstringwhere-clausesql-like

MySQL Multiple CASE WHEN in WHERE clause not working


I am trying to optimize the search engine of my wensite. My data base contains phrases. @word is defined as varchar and contains a similar phrase to the one which exist in the DB. I want if the given search phrase matches exactly to the entry in DB the entry will be chosen, if nothing could be found, search with like '%phrase%' and if nothing will be find with this method, then the MATCH columnx against (phrase) method should be used. This is the code I tried with:

select distinct columnx 
  from tabley 
  where   
      ( CASE when columnx LIKE @word is not null 
             then columnx LIKE @word
             when columnx like concat('%',@word,'%') is not null
             then columnx like concat('%',@word,'%')
             else  MATCH (columnx) AGAINST (@word) END
      );    

To make sure if the cases on a standalone select query works fine I used them separately in where clause. I get result for these queries:

select distinct columnx from view_materialisiert where MATCH (columnx) AGAINST (@word);

and

select distinct columnx from view_materialisiert where columnx like concat('%',@word,'%');

And as expected no result for:

select distinct columnx from view_materialisiert where columnx like @word;

The question is why i dont get any result when I use the case condition at all?


Solution

  • If you want values that match on any of the three condition, you can use boolean logic:

    select columnx
    from tabley
    where columnx = @word
       or columnx like concat('%', @word, '%')
       or match(columnx) against (@word)
    

    Or you can extend the case logic:

    where case
        when columnx = @word                      then 1
        when columnx like concat('%', @word, '%') then 1
        when match(columnx) against (@word)       then 1
    end
    

    However I am unsure that's really what you expect. It seems like you want to iteratively check the conditions, trying the next one only when the preceding had no match on the entire table. This type of logic would be typically implemented with union all and not exists:

    select columnx
    from tabley
    where columnx = @word
    union all
    select columnx
    from tabley 
    where columnx like concat('%', @word, '%')
        and not exists (select 1 from tabley where columnx = @word)
    union all
    select columnx
    from tabley
    where match(columnx) against (@word)
        and not exists (select 1 from tabley where columnx = @word)
        and not exists (select 1 from tabley where columnx like concat('%', @word, '%'))
    

    How the database will optimize such query is highly dependent on your data and other factors. In the best case scenario, the following members will be "skipped" as soon as one member returns anything (because the not exists subquery is quite explicit about that), but there is no guarantee. You would need to assess the performance on your actual dataset.