14

I have a string containing comma separated keywords. For Example:

$keywords = 'keyword1, keyword2, keyword3';

My Table schema, named tbl_address is like this ( simplified ) :

id        INT(11)        PRIMARY KEY, AUTO INCREMENT
address   VARCHAR(250)   NOT NULL

Assume I have to use MySQLi in PHP ( not PDO ).

Here is my current approach:

$result = array();
$keyword_tokens = explode(',', $keywords);
foreach($keyword_tokens as $keyword) {
  $keyword = mysqli_real_escape_string(trim($keyword));
  $sql = "SELECT * FROM tbl_address WHERE address LIKE'%$keyword%'";
  // query and collect the result to $result
  // before inserting to $result, check if the id exists in $result.
  // if yes, skip.
}
return $result;

This approach works, but inefficient in performance. If there are a lot of keywords, it will make a lot queries.

My question is, is there a better way to achieve the same goal ? i.e. what is the simplest way to return all records with the address containing the ANY of the keywords ?

Raptor
  • 53,206
  • 45
  • 230
  • 366
  • This can help you: http://stackoverflow.com/a/9736386/1983854 – fedorqui Apr 16 '13 at 10:15
  • Combining “LIKE” and “IN”: http://stackoverflow.com/questions/1865353/combining-like-and-in-for-sql-server – Chris Apr 16 '13 at 10:18
  • 2
    @Shivian Seriously, why didn't you use my REGEXP one-liner? – Jimbo May 17 '13 at 10:44
  • If the keyword contains regular expression keywords, the query will fail, or generate unexpected result, e.g. keyword = `'|=|=|'` – Raptor May 20 '13 at 02:55
  • @Raptor Looking at the upvotes, and that you know how to write a regex, perhaps you should edit the most upvoted answer to contain a decent regex and then mark that as correct. The current 'marked' answer isn't used much by other users :-) – Jimbo Oct 30 '14 at 09:53
  • Sadly I have no permission to change accepted answer. – Raptor Oct 30 '14 at 09:58
  • 1
    Of course you do, this is your question ;) – Jimbo Nov 30 '16 at 10:06

8 Answers8

29

A simple REGEXP might be what you're after. You'd have to check how efficient it is for yourself.

SELECT * FROM tbl_address WHERE field REGEXP 'keyword1|keyword2|keyword3';

Jimbo
  • 25,790
  • 15
  • 86
  • 131
  • 1
    +1, this was exactly what I was looking for, I also learned a new `MySQL` string function! – Andrew Fox Oct 29 '14 at 10:45
  • 1
    Excellent answer, this has helped me resolve a weeks worth of research and helped me jump over 30% of my issue :) – Birdy Jan 26 '17 at 22:50
5
 SELECT * FROM user;
 +---------+----------+
 | user_id | username |
 +---------+----------+
 |     101 | Adam     |
 |     102 | Ben      |
 |     103 | Charlie  |
 |     104 | Dave     |
 +---------+----------+

 SELECT * 
   FROM user 
  WHERE FIND_IN_SET(username,'adam,ben,dave') > 0;
 +---------+----------+
 | user_id | username |
 +---------+----------+
 |     101 | Adam     |
 |     102 | Ben      |
 |     104 | Dave     |
 +---------+----------+
Strawberry
  • 33,750
  • 13
  • 40
  • 57
  • 1
    Just a pair of caveats with this approach. Can't use the LIKE syntax and doesn't trim the keywords... – arraintxo Apr 16 '13 at 10:32
3

You only need an 'OR', nothing else...

<?php

$result = array();
$keyword_tokens = explode(',', $keywords);
$keyword_tokens = array_map('mysqli_real_escape_string', $keyword_tokens);

$sql = "SELECT * FROM tbl_address WHERE address LIKE'%";
$sql .= implode("%' or address LIKE '%", $keyword_tokens) . "'";

// query and collect the result to $result
// before inserting to $result, check if the id exists in $result.
// if yes, skip.

return $result;

edit: Just to be sure you also trim the keywords

<?php

$result = array();
$keyword_tokens = explode(',', $keywords);
$keyword_tokens = array_map(
    function($keyword) {
        return mysqli_real_escape_string(trim($keyword));
    }, 
    $keyword_tokens
);

$sql = "SELECT * FROM tbl_address WHERE address LIKE'%";
$sql .= implode("%' OR address LIKE '%", $keyword_tokens) . "'";

// query and collect the result to $result
// before inserting to $result, check if the id exists in $result.
// if yes, skip.

return $result;

Also, you should also pass the db resource link to the mysqli_real_escape_string() function...

Jimbo
  • 25,790
  • 15
  • 86
  • 131
arraintxo
  • 484
  • 2
  • 12
1

The best way is to use fulltext search.

http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html

if you don't want to use fulltext you can use OR in your WHERE condition

SELECT * FROM tbl_address WHERE address LIKE '%$keyword%' OR adress LIKE '%$keyword2%'
Robert
  • 19,800
  • 5
  • 55
  • 85
  • If you have multiple keywords you can make a string like ::-> SELECT * FROM tbl_address WHERE address LIKE "%key1%key2%key3%" – Nilupul Heshan Dec 20 '21 at 06:57
1

Make single query

$keywordary = explode(',', $keywords);
foreach($keywordary as $keyword) {
  $keys = trim($keyword);
    $other .=" or address like '%$keys%'";
}
$sql = "SELECT * FROM tbl_address WHERE address LIKE'%$keyword%' $other";

execute query;
return $result;
Vaishu
  • 2,333
  • 3
  • 23
  • 25
  • 1
    Should this not be `$other .="` instead of `$other ="` inside your loop? Otherwise, you're cancelling out the `or` clause in each loop. – TheCarver Feb 18 '15 at 19:21
1

Try WHERE IN clause:

$keyword = (array)explode(',', $keywords);
for($i=0;$i=count($keyword);$i++){
   $keyword[$i]=mysqli_real_escape_string(trim($keyword[$i]),'\'" ');
}
//This is what I suggest.
$query='SELECT * FROM tbl_address WHERE address IN ("'.implode('","',$keyword).'")';

Successfully tested on MySQL 5.1.

B.F.
  • 477
  • 6
  • 9
0

Best way is just create search string WHERE clause and append it to query and run it once.

$result = array();
$keyword_tokens = explode(',', $keywords);
$where = '';$i=0
foreach($keyword_tokens as $keyword) {
  $where.= " address LIKE'%".mysqli_real_escape_string(trim($keyword))."%' OR ";
}
  // trim last OR with substr_replace
  substr_replace($where, "OR", -1, 1);
  $sql = "SELECT * FROM tbl_address WHERE $where";

return $result;
mukund
  • 2,253
  • 1
  • 18
  • 31
0

Hi create a query with union and execute in the end of the loop

$result = array();
$keyword_tokens = explode(',', $keywords);
$sql = '';
foreach($keyword_tokens as $keyword) {
  $keyword = mysqli_real_escape_string(trim($keyword));
  if (!empty($sql)) $sql .= " UNION "; 
  $sql .= "SELECT * FROM tbl_address WHERE address LIKE'%$keyword%'";
  // query and collect the result to $result
  // before inserting to $result, check if the id exists in $result.
  // if yes, skip.
}

Execute the query here.

Codesen
  • 7,724
  • 5
  • 29
  • 31