Mysql match...against vs. simple like "%term%" - mysql

What's wrong with:
$term = $_POST['search'];
function buildQuery($exploded,$count,$query)
{
if(count($exploded)>$count)
{
$query.= ' AND column LIKE "%'. $exploded[$count] .'%"';
return buildQuery($exploded,$count+1,$query);
}
return $query;
}
$exploded = explode(' ',$term);
$query = buildQuery($exploded,1,
'SELECT * FROM table WHERE column LIKE "%'. $exploded[0] .'%"');
and then query the db to retrieve the results in a certain order, instead of using the myIsam-only sql match...against?
Would it dawdle performance dramatically?

The difference is in the algorithm's that MySQL uses behind the scenes find your data. Fulltext searches also allow you sort based on relevancy. The LIKE search in most conditions is going to do a full table scan, so depending on the amount of data, you could see performance issues with it. The fulltext engine can also have performance issues when dealing with large row sets.
On a different note, one thing I would add to this code is something to escape the exploded values. Perhaps a call to mysql_real_escape_string()

You can check out my recent presentation I did for MySQL University:
http://forge.mysql.com/wiki/Practical_Full-Text_Search_in_MySQL
Slides are also here:
http://www.slideshare.net/billkarwin/practical-full-text-search-with-my-sql
In my test, using LIKE '%pattern%' was more than 300x slower than using a MySQL FULLTEXT index. My test data was 1.5 million posts from the StackOverflow October data dump.

Related

Checking multiple columns for a concatenated match MySQL

Hi i've hit a problem with my SQL queries, i have a table that contains 3 columns, one for vehicle brands, one for models and one for model versions.
So my data is split like
BRAND || MODEL || MODEL VERSION
RENAULT || R4 || R4 1.1 GTL
I've been asked to replace our current dropdown system with an input to make it easier for users to select their vehicle.
I'm using jQuery Autocomplete and my query looks something like this.
SELECT DISTINCT CONCAT (brand, ' ', model, ' ', version) as data from vehicles WHERE brand LIKE '%Golf%' OR model LIKE '%Golf%' OR version LIKE '%Golf%' LIMIT 5
So far so good, this will output "RENAULT R4 R4 1.1 GTL" if i type in "RENAULT"... the problem here comes when the user inserts something like Renault R4 instead of just "Renault"
As they've included the Model name as well as the Brand then it doesn't really match any of my columns in the Database and my Ajax call returns no results.
I need to query the actual result set from that concat instead so that anything the users type in will match the results, but i have no idea how i can do this.
In desperation i tried to type where data LIKE '%RENAULT R4%' but as expected this also doesn't work... What can i do in this situation? Any help would be appreciated.
Easy and slow way: Split the string by spaces and ask for each word.
SELECT ...
WHERE
(brand LIKE '%Renault%' OR model LIKE '%Renault%' OR version LIKE '%Renault%')
AND (brand LIKE '%R4%' OR model LIKE '%R4%' OR version LIKE '%R4%')
LIMIT 5
Keep in mind, that query like this one does not allow use of any index, so it is very slow.
The more complicated, but much faster implementation is to use fulltext index. You need recent version of MySQL (5.6 or newer); older versions support fulltext only on MyISAM tables which are not really a database.
CREATE FULLTEXT INDEX idx ON vehicles(brand, model, version);
SELECT ... FROM vehicles
WHERE MATCH(brand, model, version) AGAINST('Renault R4')
LIMIT 5;
(Query not tested, but you should get the idea.)
I can only think of this one, but I believe there are better ways to do it.
OR CONCAT (brand, ' ', model, ' ', version) LIKE '%RENAULT R4%'

how to speed up mysql regex query

I want to develope a site for announcing jobs, but because I have a lot of conditions (title,category,tags,city..) I use a MySQL regex statement. However, it's very slow and sometimes results in a 500 internal Server Error
Here is one example :
select * from job
where
( LOWER(title) REGEXP 'dév|freelance|free lance| 3eme grade|inform|design|site|java|vb.net|poo '
or
LOWER(description) REGEXP 'dév|freelance|free lance| 3eme grade|inform|design|site|java|vb.net|poo '
or
LOWER(tags) REGEXP 'dév|freelance|free lance| 3eme grade|inform|design|site|java|vb.net|poo')
and
LOWER(ville) REGEXP LOWER('Agadir')
and
`date`<'2016-01-11'
order by `date` desc
Any advice?
You can't optimize a query based exclusively on regexes. Use full text indexing (or a dedicated search engine such as Mnogo) for text search and geospatial indexing for locations.
The big part of the WHERE, namely the OR of 3 REGEXPs cannot be optimized.
LOWER(ville) REGEXP LOWER('Agadir') can be turned into simply ville REGEXP 'Agadir' if your collation is ..._ci. Please provide SHOW CREATE TABLE job.
Then that can be optimized to ville = 'Agadir'.
But maybe this query is "generated" by your UI? And the users are allowed to use regexp thingies? (SECURITY WARNING: SQL injection is possible here!)
If it is "generated", the generate the "=" version if there are no regexp codes.
Provide these:
INDEX(ville, date) -- for cases when you can do `ville = '...'`
INDEX(date) -- for cases when you must have `ville REGEXP '...'`
The first will be used (and reasonably optimal) when appropriate. The second is better than nothing. (It depends on how many rows have that date range.)
It smells like there may be other SELECTs. Let's see some other variants. What I have provided here may or may not help with them.
See my indexing cookbook: http://mysql.rjweb.org/doc.php/index_cookbook_mysql

mysql performance difference between where id = vs where id IN()

I am very new to mysql . I had a query, from this query i am getting some stock_ids.In the second query i need to pass those stock_ids. For this i am fetching all the stock_ids in a array.Now my question is in which way can i pass those stock_ids to the second query. For this i have two approaches.
First approach:
$array_cnt = count($stockid_array);
for($i = 0; $i<$array_cnt;$i++)
{
$sql = "select reciever_id,sender_identifier,unique_stock_id,vat_number,tax_number from stocktable where stock_id = '".$stockid_array[$i]."'";
// my while loop
}
Another approach is
$sql = "reciever_id,sender_identifier,unique_stock_id,vat_number,tax_number from stocktable where stock_id in ('1','2','3','4','5','6','7','8','9');
//while loop comes here.
So which approach gives good performance ? Please guide me.
MySQL has a nice optimization for constants in an in list -- it basically sorts the values and uses a binary search. That means that the in is going to be faster. Also, in can take advantage of an index on the column.
In addition, running a single query should be faster than running multiple queries.
So, the in version should be better than running multiple queries with =.

Trick to use variable in match against mysql

Please first read my question,and then you will find out it is not a duplicate of other question.
I'm using sphinx search for 98% of search,but need to use match against for just one query.
As we know from mysql documentation that AGAINST only takes string.The search string must be a literal string, not a variable or a column name.
But I have found this link http://bugs.mysql.com/bug.php?id=66573 ,which says it is possible.But I'm not sure how to use that in my case.
Here is my code
$sqli="SELECT busi_title,category FROM `user`.`user_det`";
$queryi=mysqli_query($connecti,$sqli);
if(mysqli_num_rows($queryi)>0){
while($rowi=mysqli_fetch_assoc($queryi)){
$busi_title=$rowi['busi_title'];
$category=$rowi['category'];
}
}else{
echo "OH NO";
}
$sqlj="SELECT * FROM `user`.`user_det` WHERE MATCH(student) AGAINST('$busi_title','$category')";
$queryj=mysqli_query($connecti,$sqlj);
if(mysqli_num_rows($queryj)>0){
..............................
..............................
}else{
foreach ( $res["matches"] as $doc => $docinfo ) {
................................
...............................
}
}
MATCH() AGAINST() is giving error,as it supposed to be.How to use that trick of that link in this case.I don't know the use of #word:= of that link.
Thanks in advance.
That link doesn't show a trick to get around a limitation of MySQL. It's a bug report demonstrating an incorrect statement in the MySQL documentation. The statement in the documentation has now been corrected.
The reason you're getting an error is because you're sending two parameters to AGAINST and it only accepts one. You can use a MySQL variable in AGAINST which is what the bug report is about, but this has nothing to do with the PHP variable that you're using.
EDIT
Upon reading your response, I rather suspect that you have your syntax backwards.
SELECT * FROM `user`.`user_dets` WHERE MATCH(busi_title, category) AGAINST('student')
But note this from the documentation:
The MATCH() column list must match exactly the column list in some FULLTEXT index definition for the table, unless this MATCH() is IN BOOLEAN MODE. Boolean-mode searches can be done on nonindexed columns, although they are likely to be slow.
If you don't have a Fulltext index, you'll actually want this:
SELECT * FROM `user`.`user_dets` WHERE `busi_title` LIKE '%student%' OR `category` LIKE '%student%'
When they say "The search string must be a literal string, not a variable or a column name" does not mean you cannot use variable to create your Query String.
So it is OK to make your query very simple.
Your WHERE could be this:
WHERE `student` = $busi_title OR `student` = $category

MySQL to MongoDB conversion

How do I convert the following into MongoDB query ?
sets_progress = Photo.select('count(status) as count, status, photoset_id')
.where('photoset_id IN (?)', sets_tracked_array)
.group('photoset_id, status')
There is no 1 to 1 mapping of a SQL query to a NoSQL implementation. You'll need to precalculate your data to match the way you want to access that data.
If it is small enough, then this query will need to change into a map-reduce job. More here: http://www.mongodb.org/display/DOCS/MapReduce
Here's a decent tutorial that takes a query that GROUP's and converts to map-reduce: http://www.mongovue.com/2010/11/03/yet-another-mongodb-map-reduce-tutorial/