I am trying to fetch records in MySQL using a simple used submitted field. More precisely, the user inputs a name (firstname or lastname or fullname) and the server should return matched rows.
What I am doing so far is something like:
SELECT * FROM people
WHERE
firstname LIKE '%user_submitted_data%' OR
lastname LIKE '%user_submitted_data%'
That works well for now, but that (obviously) won't work when a user submits the fullname. Is there a way to add a OR between the whole 'WHERE type conditions' and the 'HAVING type conditions'? This way I could do something like:
SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname
FROM people
WHERE
firstname LIKE '%user_submitted_data%' OR
lastname LIKE '%user_submitted_data%' OR
HAVING fullname LIKE '%user_submitted_data%'
I know I could just split the original string but that has some negative impact since you have to deal with names containing spaces such as 'De Gaule' and stuff like that.
Just put all conditions into the HAVING clause.
SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname
FROM people
HAVING firstname LIKE '%user_submitted_data%'
OR lastname LIKE '%user_submitted_data%'
OR fullname LIKE '%user_submitted_data%
The WHERE clause could discard rows early, but since you cannot discard them until after you have evaluated the condition on the computed column, and that has to wait until HAVING, it buys you nothing to use WHERE.
Do a subquery:
SELECT [some fields]
FROM
SELECT firstname, lastname, CONCAT(firstname, ' ', lastname) as fullname
FROM people) AS tmp
WHERE firstname LIKE '%user_submitted_data%'
OR lastname LIKE '%user_submitted_data%'
OR fullname LIKE '%user_submitted_data%'
Let's consider some possible inputs:
John
Smith
John Smith
Your initial sample query is:
SELECT * FROM people
WHERE
firstname LIKE '%user_submitted_data%' OR
lastname LIKE '%user_submitted_data%'
Now, when the user enters the first input, this query will pick all the people whose first name contains 'John'; it will also pick all the people whose last name contains 'John' (for example, all the Johnsons in the database). Similarly, the second input will pick all the people whose first name contains 'Smith'; it will also pick all the people whose last name contains 'Smith' (for example, the Smithsons and Smithers). So far, so good; it isn't perfect because of case-sensitivity issues (I will ignore case-sensitivity from here on, but you probably should not ignore it at all), but it will be OK.
The third input will only pick the people whose first name contains 'John Smith'; it will also pick those people whose last name contains 'John Smith'. However, it is rather likely that there are very few people who meet those criteria - those people called John Smith will have just John in the first name and just Smith in the last name. This is unlikely to be what you had in mind.
It is not clear whether you have a column called 'fullname' in the table. If you do, then you can just match against that column instead of matching against the first name and last name separately. If you don't, maybe you can manufacture such a column and then run the query against that.
SELECT *
FROM (SELECT firstname || ' ' || lastname AS fullname, ... FROM people) AS t
WHERE t.fullname LIKE '%user_submitted_data%'
This works reasonably well.
However, if you are worried about names such as 'Charles De Gaulle' (or 'Charles de Gaulle') or 'Michael van den Berg'), then the matching will fail if someone enters 'Charles Gaulle' or 'Michael Berg', let alone Michael Vandenberg. You would probably need to replace any space characters in the user input with a '%' symbol too. Even then, you face the problem that the words must appear in exactly the sequence given by the user - which may not matter, but you should consciously decide that it doesn't matter. For example, if the input is 'Adam John Smith', then the query won't catch 'John Adam Smith'; if the input is 'Smith, John', then it won't pick up anyone (most likely).
If you want to manage this, you probably need to tokenize the user's input, and search on the separate words. Beware of someone asking about a sub-string of a word (for example, someone asks about 'de' as a name word) - none of the queries at the moment ensures that the user input words match whole words in the values (John vs Johnson), and doing so with the SQL standard LIKE operator is near enough impossible.
You can reference a computed column in the WHERE clause if you define that column in a subquery:
SELECT p.*
FROM (
SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname
FROM people
) p
WHERE
p.firstname LIKE '%user_submitted_data%' OR
p.lastname LIKE '%user_submitted_data%' OR
p.fullname LIKE '%user_submitted_data%';
But honestly, for the type of search you're doing, LIKE with wildcards is a terrible solution. You should think about using a FULLTEXT index:
CREATE FULLTEXT INDEX people_names ON people(firstname, lastname);
SELECT *
FROM people
WHERE MATCH(firstname, lastname) AGAINST( ? );
PS: FULLTEXT indexes work only with the MyISAM storage engine. Another solution, even more speedy, is to use Sphinx Search for fulltext indexing.
Although using a subquery works well, it will have an impact because you are not hitting any indexes.
What about adding a computed column (firstname || ' ' || lastname) to the table and an index to it? Surely it would be much faster.
If you cannot do that I think that querying like
WHERE firstname || ' ' || lastname LIKE '%user_submitted_data%'
should still work faster than two ORs and one subquery.
Related
I'm working on a quiz for this internship and one question is worded strangely. I'm hoping that one of you could help me find clarification.
Context: I've just created a flat-file table (database), and added 4 columns (UserId,firstname,lastname,email). I filled each column with 15 rows of made-up data.
The question states "Query all rows in the firstname column that start with the letter B and ordered by the lastname column in descending order."
I'm not sure what they're asking for, does this make sense to you?
The question is asking you to query the table, selecting the firstname column and all data that starts with the letter 'B'. And then ordered by the lastname. It's a fairly basic query:
SELECT t.firstname
FROM tablename t
WHERE t.firstname like 'B%'
ORDER BY t.lastname DESC;
EDIT: I did forget the DESC;
The query consists in retrieving first names and last names of rows where the firstname starts with the letter B, and then to order the resulting set using the lastname field in descending order.
The query in sql would be like:
SELECT firstname, lastname FROM users WHERE firstname LIKE 'B%' ORDER BY lastname DESC;
I am making a small database at the moment (less than 50 entries) and I am having trouble with a query. My query at the moment is
SELECT Name
FROM Customers
WHERE Name LIKE '%Adam%'
The names are in the format of "Adam West".
The query works fine in retrieving all the people with "Adam" in their name but I would like to only retrieve the first name, not the last name. I don't want to split the columns up but would like to know how to rewrite my query to account for this.
SELECT Name
FROM Customers
WHERE Name LIKE 'Adam%'
if you are storing name with space as separator example "Adam abcd" where 'Adam' is firstname and 'abcd' as lastname then following will work
SELECT Expr1
FROM (SELECT LEFT(Name, CHARINDEX(' ', Name, 1)) AS Expr1
FROM Customers) AS derivedtbl_1
WHERE (Expr1 LIKE 'Adm%')
for more details read this article http://suite101.com/article/sql-functions-leftrightsubstrlengthcharindex-a209089
If the name of your customers starts with first name (like Adam West) you can use
select Name
from Customers
where Name like 'Adam %'
Otherwise, if the format of the name is <last name> <first name> you can use
select Name
from Customers
where Name like '% Adam'
Try this
{SELECT Name
FROM Customers
WHERE SUBSTRING(Name,1,CHARINDEX(' ',Name)-1) LIKE '%Adam%'
}
I assumed the first name and last name is saparated by space and i took the firstname out
Using SUBSTRING(Name,1,CHARINDEX(' ',Name)-1)
and compared how you wanted. if your have something else saparating first name and last name change space in charindex with the saparater.
Please let me know if any other help needed.
Regards
Ashutosh
You should indeed "split" your columns and normalize your tables to avoid having to use complicated string functions to search for a lastname or firstname or what ever you need to look for. What about if someone entered lastname first and then firstname? Or just a nickname?
That said, check the use of LIKE on Microsoft technet site. The following query should be helpful on you case:
SELECT Name
FROM Customers
WHERE Name LIKE 'Adam%'
I have this query to retrieve the First Name out of the Full_Name field.
SELECT Employee_Table.Full_Name, Left([Full_Name],InStr([Full_Name]," ")-1) AS First_Name
FROM Employee_Table;
It works fine,
However, I tried to change the query to get the Last Name into the Last_Name field by changing the query to this one but it did not work. Please Help
SELECT Employee_Table.Full_Name, Right([Full_Name],InStr([Full_Name]," ")+1) AS Last_Name
FROM Employee_Table;
I would like to have only one query that pulls the information and not two separate ones.
Thanks
Regards
In your second query, you are pulling from the end of the string, but the length is from the beginning. Oops. The function that you want is MID() rather than RIGHT():
SELECT Employee_Table.Full_Name, Left([Full_Name],InStr([Full_Name]," ")-1) AS First_Name,
mid([Full_Name],InStr([Full_Name]," ")+1) as Last_Name
FROM Employee_Table;
I don't understand what what the purpose of using "^" in the second code?
Another problem is that I don't retrieve right information in second code compare to the first code. Why?
-- First code, use adventurework DW 2008
select FirstName, LastName from DimCustomer
where LastName like '[j-N]%'
order by LastName
-- Second code, use adventurework DW 2008
select FirstName, LastName from DimCustomer
where LastName like '[^L-N]%'
order by LastName
^ means where the character is not within the specified range.
Which will probably explain why you don't get the results you though you would?
I have a table, that contains employees. Since the company I'm working for is quite big (>3k employees) It is only natural, that some of them have the same names. Now they can be differentiated by their usernames, but since a webpage needs a drop-down with all of these users, I need to add some extra data to their names.
I know I could first grab all of the users and then run them through a foreach and add a count to each of the user objects. That would be quite ineffective though. Therefore I'm in need of a good SQL query, that would do something like this. Could a sub-query be the thing I need?
My Table looks something like this:
name ----- surname ----- username
John Mayer jmaye
Suzan Harvey sharv
John Mayer jmay3
Now what I think would be great, if the query returned the same 3 fields and also a boolean if there is more than one person with the same name and surname combination.
Adding the flag to Daniel's answer...
SELECT NAME, SURNAME, USERNAME, DECODE(COUNT(*) OVER (PARTITION BY NAME, SURNAME), 1, 'N', 'Y')
FROM
YOUR_TABLE;
Please note that Oracle SQL has no support for booleans (sigh...)
This can be easily done with a count over partition:
SELECT NAME, SURNAME, USERNAME, COUNT(*) OVER (PARTITION BY NAME, SURNAME)
FROM
YOUR_TABLE;