MySQL/ORACLE performance using LIKE and CONCAT - mysql

Which one of these two queries would perform better in MySQL and/or ORACLE?
SELECT *
FROM User
WHERE name LIKE "%searchTerm%"
OR lastname LIKE "%searchTerm%"
OR email LIKE "%searchTerm%";
or
SELECT *
FROM User
WHERE CONCAT(name, " ", lastname, " ", email) LIKE "%searchTerm%";
I have a strong feeling that the second one, but I'd like to be sure.

SELECT *
FROM User
WHERE name LIKE "%searchTerm%"
OR lastname LIKE "%searchTerm%"
OR email LIKE "%searchTerm%";
This query is better then second one. Because if you want to use different filter on all the Fields then you can use them easily.
SELECT *
FROM User
WHERE CONCAT(name, " ", lastname, " ", email) LIKE "%searchTerm%";
In this query you can not use different filter. You have to use common filter on all the fields.

First of all the two queries are not equivalent. In case that a blank (the concatenation delimiter) may be used in the searchTerm, the first query may not match, but the second can, because the match spans two or three columns.
Performance will be very same as both queries make full table scan and the difference is only in the filter condition
First Query
1 - filter("NAME" IS NOT NULL AND "NAME" IS NOT NULL AND "NAME" LIKE
'%searchTerm%' OR "LASTNAME" IS NOT NULL AND "LASTNAME" IS NOT NULL AND
"LASTNAME" LIKE '%searchTerm%' OR "EMAIL" IS NOT NULL AND "EMAIL" IS
NOT NULL AND "EMAIL" LIKE '%searchTerm%')
Second Query
1 - filter("NAME"||' '||"LASTNAME"||' '||"EMAIL" LIKE '%searchTerm%')
So basically neither query is suitable for a customer search on non trival tables.
You typically want to limit the search to column LIKE 'xxxxxx%' which can use an index.

(MySQL Answer)
Either way will involve a full table scan, so neither will be fast. (Fetching rows is more costly than evaluating expressions.) Furthermore, the leading wildcard implies that the string(s) must be fully scanned; no INDEX usable possible.
If your "serachTerm" is always word(s), then use FULLTEXT(name, lastname, email) and MATCH(name, lastname, email) AGAINST ("+searchTerm" IN BOOLEAN MODE); it will be a lot faster.

Related

Where statement with multiple 'Not Like'

I'm trying to write the following statement:
WHERE field LIKE 'Pandora' AND field Not Like 'radio', 'digital', 'internet';
Translation: Select where field is like Pandora and not like radio, digital, or internet.
Is there a way to write this statement without writing Not Like 3 times with ANDs in between?
Thank you
If "field" is not just single words, you would need to do something like this:
SELECT * FROM table WHERE field LIKE '%Pandora%' AND field NOT LIKE '%radio%' AND field NOT LIKE '%internet%' and field NOT LIKE '%digital%';
First of all, your query is redundant, in that if field is LIKE 'pandora', then the other conditions will by default return false.
There is no possible way that field can be equal to 'Pandora', 'radio', 'digital', and 'internet'.
As a result, you can simplify your query using the following example:
SELECT *
FROM example
WHERE field = 'Pandora';
If the two conditions represent two separate fields, then you can use the REGEXP operator to enforce the DRY principle while still allowing for further pattern matching:
SELECT *
FROM example
WHERE field_1 = 'Pandora'
AND field_2 NOT REGEXP '^(radio|digital|internet)$';
If you're searching for specific words, you can use NOT IN()
WHERE field LIKE 'Pandora' AND field NOT IN('radio', 'digital', 'internet');
If you need the wildcard % in your search you'll need to use multiple LIKEs.

MySQL query with extra manual output based on WHERE

Maybe it's a dumb question, but imagine having a table with fields like:
wholename, lastname, firstname, dateOfBirth
Now I want to search that table based on user input, but I would like to give a match % to the result. Meaning:
If lastname + firstname + dateOfBirth are all found in the database matchPrct = 100.
If lastname + dataofBirth are found in the databse matchPrct = 80.
And a few other rules (With other words the field matchPrct is an auto generated field which is not really in the db).
SQL for what I would like to achieve is:
SELECT * FROM table
WHERE firstname="%mike%" AND lastname="%tysson%" AND dateOfBirth="01/01/2012"
(create matchPrct=100) OR ....
Hope what I mean is clear.
LIKE is an operator that returns a boolean value, = behaves similarly. Booleans in MySQL are 1 for true and 0 for false. That means that you can say this:
select *,
((firstname like '%mike%') + (lastname like '%tysson%') + (dateOfBirth = '01/01/2012')) / 3.0 as score
from table
where firstname like '%mike%'
or lastname like '%tysson%'
or dateOfBirth = '01/01/2012'
And then read your "how well did it match" values out of score. You can also apply individual weights to each component so that a last name match would count for more than a first name match.
In databases that don't use 1 and 0 as booleans you can cast them or use a CASE to convert booleans to the numbers you want. This doesn't apply to MySQL of course but it is worth keeping in mind.

Override column value in WHERE clause?

I was wondering if it was possible to override column value in the Where clause of a SQL query (MySQL in my case).
To be more clear, here is an example :
Suppose a basic query is :
SELECT lastname, firstname FROM contacts WHERE lastname = "Doe";
Is it possible to force lastname and firstname to return value from an other table, just by modifying what is after the WHERE part ? Something like
SELECT lastname, firstname FROM contacts WHERE lastname = (SELECT name FROM companies);
I am currently testing a web application, and I found a SQL Injection flaw where I can change Doe to whatever I want, but I'm limited with only one query (mysql_query restriction of PHP) and addslashes (so no " and ').
possible could be
SELECT lastname, firstname FROM contacts WHERE lastname = "{0}" UNION SELECT {1} --
where {0} non existed value and {1} data from other tables
UPDATE from wiki example
$res = mysql_query("SELECT author FROM news WHERE id=" . $_REQUEST['id'] ." AND author LIKE ('a%')");
become
SELECT author FROM news WHERE id=-1 UNION SELECT password FROM admin/* AND author LIKE ('a%')
The syntax that you used in your SELECT ... WHERE clause is a standard SQL feature called a subquery.
In the context of your example there is a restriction on the subquery to return just single value. Otherwise your query is a valid SQL and you can change subquery to return multiple values (with implicit OR) using IN operator like this:
SELECT lastname, firstname FROM contacts
WHERE lastname IN (
SELECT name FROM companies
);
You can dig deeper into this subject to uncover correlated subquery.

SQL query to search in a concated string

In a database i have a table prospect and has two columns firstname and lastname.
Now the issue is that i want to search in both columns; the easy solution would be to use a query like
SELECT * FROM `prospect` WHERE lastname like '%piece of lastname%' or firstname like '%piece of firstname%'
This however requires to have two search fields, firstname and lastname. I want that users can search in one field. How should a query look like when I want to achieve this?
Do you mean you want to search the concatenation of two fields? Then you can use something like:
SELECT * FROM prospect
WHERE CONCAT(firstname,' ',lastname) LIKE '%ohn Smit%'
Is this is what you are looking for?
SELECT * FROM prospect
WHERE firstname + ' '+ lastname LIKE '%name%'

MySQL Search Query on two different fields

I need to search on two fields using LIKE function and should match also in reverse order. My table uses InnoDB which dont have Full text search.
Consider the following case:
I have users table with first_name and last_name column. On it, there is a row with the following value:
{
first_name: 'Ludwig',
last_name: 'van Beethoven',
}
Cases:
Can search "Ludwig van Beethoven"
Can search "Beethoven Ludwig"
Can search "Ludwig"
Can search "Beethoven"
I tried this SQL statement but no luck.
SELECT CONCAT(first_name, ' ', last_name) as fullname
FROM users
WHERE fullname LIKE '%Ludwig van Beethoven%';
You need to re-state the concat expression in your where clause.
SELECT CONCAT(first_name, ' ', last_name) as fullname
FROM users
WHERE CONCAT(first_name, ' ', last_name) LIKE '%doe%';
Unfortunately "as" just create a column alias, not a variable that you can use elsewhere.
The Main Thing
Make sure you have a compound index on first_name and last_name. Otherwise, it's really easy to end up doing a full table scan regardless of how you approach this. So if you don't already have one, create one:
CREATE INDEX users_firstandlast ON users(first_name, last_name);
Syntax Options
Once that index is in place, you have some options:
Option 1: As Willis Blackburn said, repeat the CONCAT in your WHERE clause (because AS doesn't create a name you can use in the WHERE clause):
SELECT CONCAT(first_name, ' ', last_name) as fullname
FROM users
WHERE CONCAT(first_name, ' ', last_name) LIKE '%doe%';
Use EXPLAIN to check in your specific situation, but in my tests it says it uses the compound index, even though you're using a function in the WHERE clause.
Option 2: In this particular case, you can always just use two LIKE s in your WHERE clause:
SELECT CONCAT(first_name, ' ', last_name) as fullname
FROM users
WHERE first_name LIKE '%doe%' or last_name LIKE '%doe%';
Again this can make use of the compound index (whereas it won't make use of individual indexes on the first_name and last_name columns -- it would if you weren't leading with a wildcard, but according to EXPLAIN [and your mileage may vary, always check], in that case it goes with the table scan).
Option 3 In his answer, Andy says you can use HAVING for this. My read of the MySQL manual suggests it will first build the result set, and only then apply HAVING at the very end before sending it to the client, and so I'd be leery of this. But, in my quick and dirty tests, EXPLAIN tells me that if you have the compound index I mentioned above, the HAVING version does an index search, not a table scan. If your tests with real data bear that out, that may be a good option for you. This use of HAVING in this way is a MySQL extension (not standard), but then again, so is CONCAT so we're already into MySQL-specific stuff. :-) But again, double-check in your real life environment.
Conclusion
Create the index if you don't already have it, then I'd go with Option 2 if it's remotely a possibility; otherwise, option 1 unless you can find (or Andy can provide) a reference for the HAVING thing not building a massive interim result set (it would be really cool, if non-standard, if it didn't). Regardless, check with EXPLAIN, and test, in your specific environment.
SELECT * FROM users WHERE CONCAT(first_name, ' ', last_name) LIKE '%Ludwig%' OR CONCAT(last_name, ' ', first_name) LIKE '%Ludwig%';
All search cases where returned including 'Beethoven Ludwig'.
When you CONCAT() two columns the LIKE become case sensitive. So this should find you results but isn't optimal for performance:
SELECT CONCAT(first_name, ' ', last_name) AS fullname FROM users
WHERE LOWER(CONCAT(first_name, ' ', last_name)) LIKE LOWER('%doe%');
That's getting MySQL to do work on each row though.