I have a simple request: search by a user name.
Nevertheless, imagine I have this data on database:
1 - John Amazing Doe
2 - John Stupid Doe
3 - John Anthony
When I search by "John Doe", is there an easy way to search by results that have "john" or "doe"? Even better, if we could rank by who has more matches?
Thanks
Prepare the data prior to building the query
This is something that would be best handled a bit programmatically prior to sending to MySQL.
Turn the name into a list, break on space:
"John Doe" --> "John|Doe"
SELECT username FROM users WHERE username RLIKE 'John|Doe'
Be careful about sql injection here.
php Example
$db = new PDO([conn info]);
$searchArg = 'John Doe';
$nameRegex = str_replace(' ','|', strtolower($searchArg));
$userSearch = $db->prepare('SELECT username FROM users WHERE LOWER(username) REGEXP :nameRegex');
$userSearch->bindValue(':nameRegex', $nameRegex, PDO::PARAM::STR);
$userSearch->execute();
foreach ( $userSearch->fetchAll(PDO::FETCH_ASSOC) as $user ) {
echo $user['username'] . '\n<br>';
}
A full text index and MATCH() is your best option. That said, here is another way:
You can search for names that have 'John' or 'Doe' using LIKE:
SELECT user_name
FROM users
WHERE user_name LIKE 'John%'
OR user_name LIKE '%Doe'
Use leading and trailing % only if names will not always start with 'John' or end with 'Doe', otherwise use one. Double % will not make use of any indexes you have on user_name
You can then rank these using UNION and sorting column:
SELECT user_name, 1 as Sort_Col
FROM users
WHERE user_name LIKE 'John%'
AND user_name LIKE '%Doe'
UNION
SELECT user_name, 2 as Sort_Col
FROM users
WHERE (user_name LIKE 'John%'
OR user_name LIKE '%Doe')
AND user_name NOT IN (
SELECT user_name
FROM users
WHERE user_name LIKE 'John%'
AND user_name LIKE '%Doe')
ORDER BY Sort_Col ASC
You'll have to use an annoying NOT IN for the second query to be sure that you don't return rows that have both 'John' and 'Doe'. Normally the UNION would suffice, but since we added Sort_Col it will not. Just another reason to add a full text index if at all possible.
Related
SELECT * FROM users WHERE REPLACE(username, '.', '_') = 'username_without_dots'
I read about $replaceAll but it is not helping.
In my query, first I am replacing existing record and then comparing, e.g there are usernames like
harry.potter
j.k rowling,
robert downy jr.
so what will happen is that DOTs(.) in these names will be replaced by UNDERSCORE( _ ) so the result will be:
harry_potter
j_k rowling,
robert downy jr_
after that comparing with searched string.
So lets say I am searching harry_potter
SELECT * FROM users WHERE REPLACE(username, '.', '_') = 'harry_potter'
Output will be:
harry.potter
Maybe try this:
db.users.find({$and:[{"username":/^[^.]*$/},{"username":"username_without_dots"} ] } )
I have a search query to search my users table for the search term entered by the user.
My query searches in 3 rows:
user_last_name
user_first_name
user_business_address
Everything works fine as long as the user searches for one of the rows at a time.
Let's say user_first_name is 'Alex' and user_last_name is 'Smith'. If I search for 'Alex' or 'Smith' everything works fine.
The Problem
When the user searches for 'Alex Smith' as soon as the space key is hit there are no results shown anymore.
How can I search in both rows?
How do I deal with the whitespace?
My search query
$term = strip_tags('%'.$_GET['term'].'%');
$query = $database->prepare("SELECT COUNT(*) FROM users WHERE user_last_name LIKE :term OR user_first_name LIKE :term OR user_business_address LIKE :term ORDER BY user_id LIMIT 10");
$query->execute(array(':term' => $term));
$count = $query->fetchColumn();
if ($count >= 1) {
$query2 = $database->prepare("SELECT * FROM users WHERE user_last_name LIKE :term OR user_first_name LIKE :term OR user_business_address LIKE :term ORDER BY user_id LIMIT 10");
$query2->execute(array(':term' => $term));
$results = $query2->fetchAll();
}
Please let me know if you would like to see any more code.
I would be very thankful for any kind of help!
First of all, don't use LIKE to look for an exact match, use a single equal sign (=) .
Secondly, you can use CONCAT to combine first name and last name :
SELECT * FROM users
WHERE concat(user_first_name,' ',user_last_name) = :term
ORDER BY user_id LIMIT 10
I removed the last condition user_business_address like :term since you didn't explain what is it for.
If the user will input either first name/last name or full name, then you can use the LIKE operation with % wildcard :
SELECT * FROM users
WHERE concat(user_first_name,' ',user_last_name) LIKE concat('%',:term,'%')
ORDER BY user_id LIMIT 10"
Here is a document on like operator .
SELECT * from users where user_first_name = SUBSTRING_INDEX(SUBSTRING_INDEX(fullname, ' ', 1), ' ', -1) ,
TRIM( SUBSTR(fullname, LOCATE(' ', fullname)) ) AS last_name FROM registeredusers
There's a bunch of cool things you can do with substr, locate, substring_index, etc. Check the manual for some real confusion. http://dev.mysql.com/doc/refman/5.0/en/string-functions.html
Say I have a table called People with columns PersonID and Name and I can select a Person's Name like:
SELECT Name FROM People WHERE PersonID = 1
which for this example will return 'John'.
I also have another table called ForumPosts with the fields ForumPostID and PostContent where PostContent is just TEXT which for the purpose of this example can be something like "My Name is John" or "John likes football"
Now I want to perform a Query which based on a given initial PersonID will return all rows from ForumPosts where the Person's Name matches a word contained in the PostContent field.
A regex which will match single words (or in this case the person's name) is:
[[:<:]]*Person'sNameHere*[[:>:]]
So ideally I want my SQL logic to be something like:
Select * FROM ForumPosts WHERE PostContent
REGEX [[:<:]](SELECT Name FROM People WHERE PersonID = '1') [[:>:]]
However I am not sure if this is even possible or how I would structure the query.
Sounds like you want to create a regex dynamically. Regexes are just strings in MySQL, so you can just use CONCAT to create the string you want.
SELECT *
FROM ForumPosts
WHERE PostContent
REGEXP CONCAT('[[:<:]]',(SELECT Name FROM People WHERE PersonID = '1'),'[[:>:]]')
Even better, you can use a JOIN instead of a subquery
SELECT ForumPosts.*
FROM ForumPosts
JOIN People ON PersonID = 1
WHERE PostContent REGEXP CONCAT('[[:<:]]',People.Name,'[[:>:]]')
DEMO: http://sqlfiddle.com/#!2/40828/1
You can implement this logic using an exists subquery. If I understand your logic correctly:
select fp.*
from ForumPosts fp
where exists (select 1
from people p
where personid = '1' and
fp.PostContent regex concat('[[:<:]]', name, '[[:>:]]')
)
SELECT *
FROM customers
WHERE Firstname LIKE 'George'
The problem is that i have more than 1 rows in the table with tha name Geoge and the result of the query shows only one row
You will want to include the wildcard % character to include the rows the have George present in the name:
SELECT *
FROM customers
WHERE Firstname LIKE '%George%';
If George will always appear at the beginning, then you can include the wildcard on the end:
SELECT *
FROM customers
WHERE Firstname LIKE 'George%';
you need to add a wildcard character % to match any value that contains george
SELECT *
FROM customers
WHERE Firstname LIKE '%George%'
MySQL LIKE Operator
the statement
WHERE Firstname LIKE 'George'
is equivalent with
WHERE Firstname = 'George'
that is why you are only getting one record which firstname is george.
UPDATE 1
SQLFiddle Demo
try
LOWER(Firstname) LIKE '%george%'
handles partial values and avoids case sensietivity issues.
I have a simple query as listed below
SELECT id, name, email FROM users WHERE group_id = 1
This works great until, I then start adding LIKE queries, chained with OR statements to the end.
SELECT id, name, email FROM users
WHERE group_id = 1
AND id LIKE $searchterm
OR name LIKE $searchterm
OR email LIKE $searchterm
Suddenly my WHERE clause is no longer upheld and results with a 'group_id' of 2 or 3 are retrieved.
Is there a way I can group WHERE clauses so that they are always upheld or am I missing something obvious?
Dealing with the query first - you need to use brackets for the WHERE clause to be interpreted correctly:
SELECT id, name, email
FROM users
WHERE group_id = 1
AND ( id LIKE $searchterm
OR name LIKE $searchterm
OR email LIKE $searchterm)
I'd be looking at using Full Text Search (FTS) instead, so you could use:
SELECT id, name, email
FROM users
WHERE group_id = 1
AND MATCH(id, name, email) AGAINST ($searchterm)
Mind that the USERS table needs to be MyISAM...
I assume you want
email FROM users WHERE group_id = 1 AND (id LIKE $searchterm OR name LIKE $searchterm OR email LIKE $searchterm)
Here is the mysql operator precedence table