404

I've been trying to figure out how I can make a query with MySQL that checks if the value (string $haystack ) in a certain column contains certain data (string $needle), like this:

SELECT *
FROM `table`
WHERE `column`.contains('{$needle}')

In PHP, the function is called substr($haystack, $needle), so maybe:

WHERE substr(`column`, '{$needle}')=1
Dharman
  • 30,962
  • 25
  • 85
  • 135
arik
  • 28,170
  • 36
  • 100
  • 156

8 Answers8

579

Quite simple actually:

SELECT *
FROM `table`
WHERE `column` LIKE '%{$needle}%'

The % is a wildcard for any characters set (none, one or many). Do note that this can get slow on very large datasets so if your database grows you'll need to use fulltext indices.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Wolph
  • 78,177
  • 11
  • 137
  • 148
  • 6
    This will only work if your using a prepared query. If you're using an actual string in there (ex. liquibase sql upgrade script) then consider INSTR mentioned below). This is because if your string contains a % then you'll start matching things with it to. – Ryan Shillington Oct 03 '12 at 17:51
  • 2
    i know about like queries, and yet today i wanted to find out if certain value exist in string in some column i was googling for it.. Why i never thought of it before?? – Sizzling Code Oct 09 '14 at 09:15
  • 1
    is this case sensitive? – angry kiwi Jan 21 '15 at 12:10
  • 4
    @angry_kiwi: with `column LIKE '...'` it is case insensitive, with `column LIKE BINARY '...'` it is case sensitive – Wolph Jan 21 '15 at 12:11
  • 3
    I'm surprised that `LIKE` is proposed to check for any substring since this operator uses two wildcard characters : `%` and `_`. This mean if your string $needle contains one of this special characters then the results is not as expected at all. (-1) for this reply, and (+1) for the INSTR reply. – Skrol29 Nov 30 '16 at 02:46
  • @Skrol29: A simple reason for using `LIKE` instead of `INSTR` is that `LIKE` can use an index in the case of `LIKE '...%'` whereas `INSTR` will always resort to a table scan. – Wolph Dec 01 '16 at 13:26
  • @Wolph: LIKE cannot use indexes since there is a wildcard at the start of the pattern to search. LIKE can use index only for the part of the pattern before the first wildcard. But first of all, this answer simply produce a wrong result. – Skrol29 Dec 04 '16 at 12:22
  • @Skrol29: yes, and if you re-read my comment carefully you'll notice that is exactly what I said. – Wolph Dec 04 '16 at 15:31
  • Unfortunately, this does not answer the question for me personally, I think. What if the search substring contains the wildcard placeholders - how can I unescape them if I want to include them as-is to the search?. Also, if I remember correctly, there is somewhere in MySQL a setting, which returns results only if the search string is big enough (I think it was by default 3 chars). Is this correct and if yes, is it possible to change this value "client-side" (in the PHP code) somehow? – StanE Nov 11 '17 at 21:37
  • Please don't do this with a direct string replacement, as it opens up for SQL injections. Consider using a prepared statement instead. – Jonas Hoffmann Nov 04 '20 at 13:57
205

Use:

SELECT *
  FROM `table`
 WHERE INSTR(`column`, '{$needle}') > 0

Reference:

OMG Ponies
  • 325,700
  • 82
  • 523
  • 502
  • surely LIKE is faster than INSTR? – chris Apr 08 '10 at 17:58
  • 25
    @oedo: Depends. `LIKE %...%` won't use an index if one is present, so they should be equivalent; `LIKE ...%` would use an index if present. If performance is a real concern, Full Text Search (FTS) would be a better approach. – OMG Ponies Apr 08 '10 at 17:59
  • perfect. just what I've been looking for. – arik Apr 08 '10 at 19:59
  • 3
    I like this solution as in fact there is no way to sort things according to the presence of a substring with `like` operator . With `instr` a phrase can be ordered like this `select * from table order by instr(col1,"mystring")` – Radacina Aug 01 '17 at 18:16
  • 1
    I wanted to search for _ in a field and it worked. Thanks – Safeer Ahmed Sep 04 '18 at 05:35
69
WHERE `column` LIKE '%$needle%'
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
chris
  • 9,745
  • 1
  • 27
  • 27
  • 5
    when searching for the character _ (underscore) the query LIKE '%_%' doesn't work, for some reason it returns all strings even without _ – Wojtek May 18 '17 at 12:20
  • 5
    @Wojtek _ is a wildcard for any single character, so if you want to search for a literal underscore you will need to escape it. See [MySQL LIKE query with underscore](https://stackoverflow.com/q/22167132/3427178). – jkmartindale Jun 20 '19 at 18:16
49

Mine is using LOCATE in mysql:

LOCATE(substr,str), LOCATE(substr,str,pos)

This function is multi-byte safe, and is case-sensitive only if at least one argument is a binary string.

In your case:

SELECT * FROM `table`
WHERE LOCATE('{$needle}', `column`) > 0
Dharman
  • 30,962
  • 25
  • 85
  • 135
risnandar
  • 5,513
  • 1
  • 26
  • 17
  • 14
    'column' should be column (without quotes) – Wojtek May 18 '17 at 12:16
  • 'column' should not be in backticks, right? – Valter Ekholm May 25 '21 at 14:29
  • 3
    @ValterEkholm, No. backticks are the normal way to denote an entity in MySQL. (Like a schema, table, table-alias, column, etc.) They're especially useful when you have a column named something that's a keyword or very common/ambiguous term (e.g. DateTime, Date, Status, Name, etc). Wrapping them in backticks makes it explicitly clear that it's an entity, and not a keyword/function/etc. – Troy Niemeier Jan 20 '22 at 16:37
13

In addition to the answer from @WoLpH.

When using the LIKE keyword you also have the ability to limit which direction the string matches. For example:

If you were looking for a string that starts with your $needle:

... WHERE column LIKE '{$needle}%'

If you were looking for a string that ends with the $needle:

... WHERE column LIKE '%{$needle}'
whoan
  • 8,143
  • 4
  • 39
  • 48
Joshua Powell
  • 894
  • 1
  • 9
  • 13
5

You probably are looking for find_in_set function:

Where find_in_set($needle,'column') > 0

This function acts like in_array function in PHP

Lexib0y
  • 519
  • 10
  • 27
Andres
  • 83
  • 1
  • 1
4

be aware that this is dangerous:

WHERE `column` LIKE '%{$needle}%'

do first:

$needle = mysql_real_escape_string($needle);

so it will prevent possible attacks.

Alejandro Moreno
  • 5,578
  • 2
  • 31
  • 29
  • 7
    *Some possible attacks. Also, `mysql_real_escape_string` is going to be deprecated in future PHP releases. – Jack Feb 03 '14 at 21:24
  • 12
    You should use [prepared statements](http://au1.php.net/pdo.prepared-statements), and leave the escaping to PHP. `$stmt = $dbh->prepare("Where 'column' LIKE '{:needle}'"); $stmt->bindParam(':needle', $needle); $stmt->execute();` – cloudworks Mar 03 '14 at 05:25
-3

The accepted answer would be correct for MySQL alone, but since the question is using:

  • a variable,
  • appears to be using {$needle} as a replacement tag, and
  • it mentions PHP

it appears the author wanted to construct the MySQL query using PHP.

Since the question was asked 12 years ago, current practice would be to use preprepared statements to prevent SQL injection.

Here is an example with PHP:

function check_connection ($user, $pass, $db = 'test', $host = '127.0.0.1', $charset = 'utf8mb4') {
     if (isset($GLOBALS['conn']) && is_object($GLOBALS['conn']) && ($GLOBALS['conn'] instanceof PDO)) {
          if (same_database($db) === true) {
               $connection = &$GLOBALS['conn']; 
          }
          else {
               $GLOBALS['conn'] = pdo_connect($user, $pass, $db, $host, $charset);
               $connection = &$GLOBALS['conn'];       
          }
     }
     else {
          $GLOBALS['conn'] = pdo_connect($user, $pass, $db, $host, $charset);
          $connection = &$GLOBALS['conn'];
     }

     return $connection;
}

function pdo_connect ($user, $pass, $db, $host, $charset){    
     $dsn = "mysql:host=$host;dbname=$db;charset=$charset";
     $options = [
       PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
       PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
     ];
     try {
       return new PDO($dsn, $user, $pass, $options);
     } 
     catch (\PDOException $e) {
       throw new \PDOException($e->getMessage(), (int)$e->getCode());
     }
}

function same_database($db) {
    if (isset($GLOBALS['conn']) && is_object($GLOBALS['conn']) && ($GLOBALS['conn'] instanceof PDO)) {
        $sql = "SELECT DATABASE() AS 'database'";
        $sth = $GLOBALS['conn']->prepare($sql);
        $sth->execute();
        if (strcasecmp(trim($sth->fetchAll(PDO::FETCH_ASSOC)['0']['database']), trim($db)) === 0) { 
            return true;
        }
    }

    return false;
}
    
$conn = check_connection($user, $pass, $db, $host, $charset);

$sql = "
     SELECT *
     FROM `table`
     WHERE `column` like :needle
";

// Concatenating the % wildcard before and after our search variable
$bind = array(
     ':needle' => '%'.$needle.'%'
);

$sth = $conn->prepare($sql);
$sth->execute($bind);

// Being redundant about fetch_assoc incase it was not set in pdo() options
$result = $sth->fetchAll(PDO::FETCH_ASSOC);

// You would use rowCount(), instead of fetchAll(), if it is NOT a SELECT statement
// $sth->rowCount();

print_r($result);

Here are two resources for building PHP PDO statements: