You are viewing revision #6 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.
It's common to see users wishing to make substring DB queries, using the %
metacharacter used to match anything; in this FAQ we'll search the tbl_comments
table from the blog tutorial hoping to find the text in $match
in the content
column
A frequently seen yet incorrect effort:
$q = new CDbCriteria( array(
'condition' => "content LIKE '%:match%'", // DON'T do it this way!
'params' => array(':match' => $match)
) );
$comments = Comments::model()->findAll( $q ); // finds nothing
fails to find anything. And the version without single quotes around the :match
yields an SQL error.
This search is properly achieved by moving the %
metacharacters into the bound parameter:
$match = addcslashes($match, '%_'); // escape LIKE's special characters
$q = new CDbCriteria( array(
'condition' => "content LIKE :match", // no quotes around :match
'params' => array(':match' => "%$match%") // Aha! Wildcards go here
) );
$comments = Comments::model()->findAll( $q ); // works!
Just as in any other prepared statement, the :match
in the condition should not be surrounded by quotes - Yii handles this parameter binding properly.
Alternate versions: ¶
$match = addcslashes($match, '%_'); // escape LIKE's special characters
// directly into findAll()
$comments = Comments::model()->findAll(
'content LIKE :match',
array(':match' => "%$match%")
);
// merging in a parameterized scope:
$this->getDbCriteria()->mergeWith( array(
'condition' => 'content LIKE :match',
'params' => array(':match' => "%$match%")
) );
Better: use CDbCriteria::addSearchCondition() ¶
If the query uses a direct CDbCriteria
object, it's more efficient (and just as safe) to do a simple substring match with the [CDbCriteria::addSearchCondition()] method, which handles it all directly.
$q = new CDbCriteria();
$q->addSearchCondition('content', $match);
$comments = Comments::model()->findAll( $q );
Yii will turn this criteria into a LIKE query using %
wildcards before and after the match variable. By default, it protects active characters in the variable.
One could also use $q->compare('content', $match, true)
but it has a few side effects. For instance, if $match
is "<>hello"
, the criteria will find the rows where the content is NOT LIKE '%hello%'
.
Warning ¶
The pattern LIKE '%XXX%'
must not be abused.
- This is not scalable and can be really slow on medium-sized databases.
For instance, MySQL cannot use indexes with such criteria, so it has to process the whole data. - This is not a google-like search, and complex queries like
zend -framework "php core"
are not possible. Users often expect more than just a substring search.
While both Postgresql and MySQL include an embedded full-text search, this kind of search is often handled by another service. The most well-known free systems are Xapian, Sphinx and Lucene.
There are already some yii extensions for this:
Another variant using CDbCriteria
CDbCriteria::addSearchCondition
thanks!
Thanks, it save my day :D
Thanks for this guidance
I have a question how can I perform a search with a string match like this Manhattan, NY
Thanks
Warning
Since v5.11.6 MySQL includes indexes and especially FULLTEXT search
php pdo prepare repetitive variables
Using $ this-> params in the query must be unique for each condition, as shown below:
http://stackoverflow.com/a/7604080
Tnx :) CONCAT is another way
Another way to have the LIKE expressions (especially RLIKE(REGEXP)) within the long Queries is to CONCAT
$db-> createCommand("SELECT ... WHERE ... LIKE CONCAT('%', :param1, '%', :param2) ESCAPE '='") ->query(array( ':param1' => YourTools::EscapeLikeWildCards($thisModel->attribute1, '='), ':param2' => YourTools::EscapeLikeWildCards($thisModel->attribute2, '='), ))
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.