Mysql RegExp question selecting from a list of codes - mysql

I am trying to match a list of motorcycle models to a series of ebay codes for listing motorcycles in ebay.
So we get a motorcycle model name that will be something like:
XL883C Sportster where the manufacturer is Harley Davidson
I have a list of ebay codes that look like this
MB-100-0 Other
MB-100-1 883
MB-100-2 1000
MB-100-3 1130
MB-100-4 1200
MB-100-5 1340
MB-100-6 1450
MB-100-7 Dyna
MB-100-8 Electra
MB-100-9 FLHR
MB-100-10 FLHT
MB-100-11 FLSTC
MB-100-12 FLSTR
MB-100-13 FXCW
MB-100-14 FXSTB
MB-100-15 Softail
MB-100-16 Sportster
MB-100-17 Touring
MB-100-18 VRSCAW
MB-100-19 VRSCD
MB-100-20 VRSCR
So I want to match the model name against the list above using a regExp pattern.
I have tried the following code:
SELECT modelID FROM tblEbayModelCodes WHERE
LOWER(makeName) = 'harley-davidson' AND fnmodel REGEXP '[883|1000|1130|1200|1340|1450|Dyna|Electra|FLHR|FLHT|FLSTC|FLSTR|FXCW|FXSTB|Softail|Sportster|Touring|VRSCAW|VRSCD|VRSCR].*' LIMIT 1
however when I run the query I would expect the code to match on either MB-100-1 for 883 or MB-100-16 for Sportster but when I run it the query returns MB-100-0 for Other.
I am guessing that I have the pattern incorrect, so can anybody suggest what I might need to do to correct this?
Many thanks
Graham

[chars] matches any of the characters 'c','h','a','r','s'
So by giving it such a long list, it will inevitably match just the first item (single character)
Try this instead
LOWER(makeName) = 'harley-davidson' AND fnmodel REGEXP '(883|1000|1130|1200|1340|1450|Dyna|Electra|FLHR|FLHT|FLSTC|FLSTR|FXCW|FXSTB|Softail|Sportster|Touring|VRSCAW|VRSCD|VRSCR).*' LIMIT 1
You might also consider not using REGEX and using FIND_IN_SET instead.

Not really fully tested, but it should be something like this:
REGEXP '^MB-[0-9]+-[0-9]+[[:space:]]+(883|1000|1130|1200|1340|1450|Dyna|Electra|FLHR|FLHT|FLSTC|FLSTR|FXCW|FXSTB|Softail|Sportster|Touring|VRSCAW|VRSCD|VRSCR)$'
In detail:
^MB- Starts with MB-
[0-9]+ One or more digits
- Dash
[0-9]+ One or more digits
[[:space:]]+ One or more white space
(883|1000|...)$ Ends with one of these
Here's the reference for the regexp dialect spoken by MySQL:
http://dev.mysql.com/doc/refman/5.1/en/regexp.html
Answer to comment:
If you want to match the Sportster row them remove all other conditions. And you may not even need regular expressions:
WHERE fnmodel LIKE '% Sportster'

Related

Matching strings without space and punctuation in MySQL

I'm working on a query which I thought should be quite intuitive, but somehow I'm facing a bit of issues when implementing it. I guess what I'm trying to achieve is to match a string stored in MySQL DB without space and punctuation (other creative approaches are more than welcome). At the same time I would like the query to handle Unicode characters in diacritics insensitive fashion (so options like REGEXP are kinda out of luck). And the last condition is I'm on MySQL 5.5 with InnoDB engine, so full-text indexing is not supported (but I'm open to upgrade to 5.6/5.7 if it helps sorting this out).
Consider the scenario which the string Hello-World from John Doe is stored in DB. I would like to find it when given the search string HelloWorld or JohnDoe. To be more general, the string in DB can contain brackets, understores and any other punctuation (not limited to ASCII but can compromise for now), while the search string can be a combination of words with or without any separators in between. The closest I've gotten so far is to daisy chain the REPLACE function for a list of known punctuation, like below:
SELECT text FROM table WHERE REPLACE(REPLACE(text, '-', ''), ' ', '') LIKE '%JohnDoe%'
My questions are:
Is there a better way instead of using the daisy chain above?
If that's the only solution, how will the performance be impacted when I chain up hundred or more REPLACE functions?
Thanks in advance for your help.
I don't know how restrictive your searches must be, but you could try to strip out all non-alphanumeric characters from it, so that you end up with a string like "HelloWorldfromJohnDoe" that you match with instead.
Have a look at this answer: How to remove all non-alpha numeric characters from a string?
You might have to change it around a bit though to make it fir your purposes. I changed it from CHAR(32) to CHAR(255) to make sure I could get the column, but you might want to look into changing the function altogether to fit your data more precisely.
Then you something like this:
SELECT *
FROM testing
WHERE alphanum(test) LIKE CONCAT('%', alphanum('John Doe'), '%')
which should give you a hit.
Method 1
I would have another column on the schema containing an "hashed" version of the name, for example, let's say you have the user:
John Doe The Great
This name hashes to
johndoethegreat
The hash function is coded in such a way that all of the following strings:
John_Doe_THE_great
John Doe The GREAT
John.Doe.The.Great
johnDOE___theGreat
john Doe the great
___john____DOE____THE____great
hash to the same value
johndoethegreat
It's trivial to write such a function. This way you can get the user input, hash it and then compare it against the hash column in your database
Names like:
Jon Doe
John Doo
will not be found of course
Method 2
Use the FULLTEXT search feature built-in in MySQL, sort the results by score and pick the first non zero entry
http://blog.oneiroi.co.uk/mysql/php/mysql-full-text-search-with-percentage-scoring/
I am totally missing the point of your question. You appear to have the string:
Hello-World from John Doe
If you want to find this when the search string is JohnDoe or John Doe, then you only need to substitute spaces:
where replace(text, ' ') like concat('%', 'JohnDoe', '%')
If you want a string that contains both "John" and "Doe" in that order, then:
where replace(text, ' ') like concat('%', 'John%Doe', '%')
I fail to see why 100 nested replace()s would be needed.

Why is dot metacharactor of regex not working in mysql?

I have two records with names bowser and Tommy in the table pet. Now when I run the following query in cmd nothing happen:
SELECT * FROM pet WHERE name LIKE '.%';
On the other hand the following query matches Bowser record:
SELECT * FROM pet WHERE name LIKE 'b%';
As far as I know, . should match any character. So '.%' should match every word.
Why is dot metacharactor of regex not working in mysql?
The reason your query doesn't work is because it is looking for names that start with a period. That is how LIKE works.
Use RLIKE or REGEXP:
WHERE name REGEXP '.*';
LIKE uses the ANSI standard for the operator. The equivalent of . is _. However, the way LIKE works is different from regular expressions.
The documentation does a good job of explaining the differences between LIKE and REGEXP.

Issue with RegExp matching a phone number format in query

I am trying to write a RegExp for MySQL that will only return the following 3 phone formats:
+1 000-000-0000 x0000
+1 000-000-0000
000-000-0000 x0000
000-000-0000
So basically:
+[any number of digits][space][any three digits]-[any three digits]-[any four digits][space][x][any number of digits]
The country code and the extension are optional. I am new to these but I would think this should return at least a number including both country code and extension options, but I get 0 results when I execute it.
\x2B[0-9]*\x20[0-9]{3}\-[0-9]{3}\-[0-9]{4}\x20x[0-9]+|
\x2B[0-9]*\x20[0-9]{3}\-[0-9]{3}\-[0-9]{4}|
[0-9]{3}\-[0-9]{3}\-[0-9]{4}
Can someone tell me why I am getting 0 results even though I have records like +1 555-555-5555 x5555 in my db. Also what is the syntax to make the country code and the extension are optional
Please note I am using [0-9] because I am querying a text field and \d didn't seem to return anything even when my criteria was something simple like \d*
So, as a joint effort, turns out the answer is here:
SELECT * FROM table
WHERE field
REGEXP '(^[+][0-9]+\ )?([0-9]{3}\-[0-9]{3}\-[0-9]{4})(\ x[0-9]+$)?'
First was the fact that character codes in mysql (x20, x2B and the like) are not allowed. Next important step was the use of parenthesis and the "?" token to make the different sections optional.
============
I think the issue is the lack of parenthesis to define the subexpressions.
This seems to work out for me, though it doesn't look real pretty:
SELECT '+1 000-000-0000 x0000' REGEXP '^(([+][0-9]*\[ ][0-9]{3}\-[0-9]{3}\-[0-9]{4}[ ]x[0-9]+)|())$'
as does
SELECT '' REGEXP '^(([+][0-9]*\[ ][0-9]{3}\-[0-9]{3}\-[0-9]{4}[ ]x[0-9]+)|())$'
as does (plain phone number):
SELECT '555-555-5555' REGEXP '^(([+][0-9]*\[ ])*[0-9]{3}\-[0-9]{3}\-[0-9]{4}([ ]x[0-9]+)*)|())$'
EDIT: (same regexp as above, but testing against version w/ country code and extension):
SELECT '+1 555-555-5555 x55' REGEXP '^(([+][0-9]*\[ ])*[0-9]{3}\-[0-9]{3}\-[0-9]{4}([ ]x[0-9]+)*)|())$'
When I try yours:
SELECT '+1 000-000-0000 x0000' REGEXP '\x2B[0-9]*\x20[0-9]{3}\-[0-9]{3}\-[0-9]{4}\x20x[0-9]+|'
I get:
1139 - Got error 'empty (sub)expression' from regexp

MySQL String Comparison with Wildcards

I wrote a query where a user can input a string and get the data related to that string back from the database.
For example, a user will input Apple even though the full name is Apple Inc.
The code would be laid out as so...
and Description like '%Apple%'
The problem with this is, it will return Snapple along with Apple.
Aside from removing the first "%" wildcard and making the user type more, how can I limit the results to just Apple?
Use a regular expression:
WHERE Description RLIKE '[[:<:]]apple[[:>:]]'
[[:<:]] matches the beginning of a word, [[:>:]] matches the end of a word.
See the documentation for all the regexp operators supported by MySQL
Firstly - string comparison with wild cards (especially leading wild cards) doesn't really scale using "like". You might want to look at full-text searching instead. This basically gives you "google-like" text searching capabilities.
To answer your question, in most cases, "Apple" is a better match than "Snapple" for the term "apple". So, you could include the concept of "match quality" in the search - something like:
select *, 10 as MatchQuality
from table
where description like 'Apple'
union
select *, 5 as MatchQuality
from table
where description like 'Apple%'
union
select *, 1 as MatchQuality
from table
where description like '%Apple%'

mysql query to match sentence against keywords in a field

I have a mysql table with a list of keywords such as:
id | keywords
---+--------------------------------
1 | apple, oranges, pears
2 | peaches, pineapples, tangerines
I'm trying to figure out how to query this table using an input string of:
John liked to eat apples
Is there a mysql query type that can query a field with a sentence and return results (in my example, record #1)?
One way to do it could be to convert apple, oranges, pears to apple|oranges|pears and use RLIKE (ie regular expression) to match against it.
For example, 'John liked to eat apples' matches the regex 'apple|orange|pears'.
First, to convert 'apple, oranges, pears' to the regex form, replace all ', ' by '|' using REPLACE. Then use RLIKE to select the keyword entries that match:
SELECT *
FROM keywords_table
WHERE 'John liked to eat apples' RLIKE REPLACE(keywords,', ','|');
However this does depend on your comma-separation being consistent (i.e. if there is one row that looks like apples,oranges this won't work as the REPLACE replaces a comma followed by a space (as per your example rows).
I also don't think it'll scale up very well.
And, if you have a sentence like 'John liked to eat pineapples', it would match both of the rows above (as it does have 'apple' in it). You could then try to add word boundaries to the regex (i.e. WHERE $sentence RLIKE '[[:<:]](apple|oranges|pears)[[:>:]]'), but this would screw up matching when you have plurals ('apples' wouldn't match '[wordboundary]apple[wordboundary]').
Hopefully this isn't more abstract than what you need but maybe good way of doing it.
I haven't tested this but I think it would work. If you can use PHP you can use str_replace to turn the spaces into keyword LIKE '%apple%'
$sentence = "John liked to eat apples";
$sqlversion = str_replace(" ","%' OR Keyword like '%",$sentence );
$finalsql = "%".$sqlversion."%";
the above will echo:
%John%' OR Keyword like '%liked%' OR Keyword like '%to%' OR Keyword like '%eat%' OR Keyword like '%apples%
Then just combine with your SQl statement
SQL ="SELECT *
FROM keywords_table
WHERE Keyword like" . $finalsql;
Storing comma delimited data is... less than ideal.
If you broke up the string "John liked to eat apples" into individual words, you could use the FIND_IN_SET operator:
WHERE FIND_IN_SET('apple', t.keywords) > 0
The performance wouldn't be great - this operation is better suited to Full Text Search.
I'm not aware of any direct solution to that type of query. But Full Text Search is a possibility. If you have a full-text index on the field of interest then a search with OR between each word in the sentence (although I think the OR operator is implied) would find that record ... but it might also find more than you want too.
I really don't think what you are looking for is completely possible but you can look into Full Text Search or SOUNDEX. SOUNDEX, for example, can do something like:
WHERE SOUNDEX(sentence) = SOUNDEX('%'+keywords+'%');
I have never tried it in this context but you should and let me know how it works out.