Searching table for fields using multiple possible combinations and wildcards - mysql

If in a simple customers table:
customers (first_name varchar(40), last_name varchar(40))
i would like to run a search by customer name, i can do:
[MYSQL...] WHERE (customers.first_name LIKE "%john%" OR customers.last_name LIKE "%john%")
However, if someone uses the search field in the front end to search for a full name, such as john arbuckle, i won't be getting any results.
What is the most effective and easiest way to run this query correctly in MySQL so that it can yield the desired results without losing the wildcard search capability?
The only thing i can think of is to split the string by whitespaces and run multiple logical comparison combinations for each string component. But is that really the way to go? Isn't there perhaps a way to combine fields when comparing their values?

Related

How to make a good sql search with like

For years when I want my user to search some field in my database where he can type anything he wants I use an algorithm to break the words and search each word separetely... a mess.
For example, if the user types in the search box "aaa bbb ccc" I dont like using:
SELECT id
FROM table
WHERE description LIKE '%aaa bbb ccc%'
Cause sometimes the user types things out of order and the query above wouldng find. What I usually do is breaking the string and concatenating it with PHP so the result becomes:
SELECT id
FROM table
WHERE description LIKE '%aaa%'
AND description LIKE '%bbb%'
AND description LIKE '%ccc%'
But today after talk to a friend I was wondering if there is some native way to do this faster using MY SQL?
What you want to do is called full text search and most relational databases support it nowadays, including mysql.
I think you can use REGEXP. For example:
Select * from table where description REGEXP 'aaa|bbb|ccc'
FULLTEXT Searches are really fast.
INSTR or locate works better than REGEXP. But it depends on various factors.
More comparison here
SELECT * from table where INSTR(description, 'aaa') >0
SELECT * from table where LOCATE(description, 'aaa') >0

Get the column name that match string comparison on multiple column-string comparison

Im building a simple search system, I have a simple form and I'm doing a query like this:
Select * from table where column_a like'%term%' or columnn_b like '%term%' or column_c like'%term%';
It is possible to determine which column was that the string %term% match (without using a bunch of if statements)?, actually I'm using CakePHP, but at this point I will not care if I need to build the query manually.
No, the identity of the matched column is lost in the evaluation of your or clauses. You'll need to do some post-processing (i.e., the "bunch of if statements" you were trying to avoid) to identify exactly which column in your result set matched.

is there a "best way" to short circuit a mysql query

I have a situation where I'm assembling a query based on user provided criteria and want to figure out what the most efficient way is to do this.
If I have a table that looks like this:
int id | varchar phone | varchar email | varchar RFID
and the user will pass in an array which defines the order (and items) with which they'd like to look up a user which *could look like this:
["id","email","phone"]
or it could look like this:
["email"]
or it could look like this:
["phone","rfid"]
or any other possible combination of those 4 fields.
Based on what I receive I need to look the user up in the order in which these fields arrived and if I find a match, I don't want to keep looking.
In other words if the input is
["email","rfid","phone"]
and I look into the db and find a user with the provided email, I don't want to keep looking to see if their rfid also matches, I just want to return said user.
However, if I don't find such an email, then I want to move on to the rfid.
So, in the various tests I've done (mostly playing with a case statement in the where clause) my results have been really terrible. Frequently taking almost a second to return a value, as opposed to taking <50ms when I simplify the where to search for the individual field.
I should note that all these fields are indexed.
So... my question is, should I just bite the bullet and make as many sql calls as there are items in the incoming array, or is there some really efficient way to structure a single query that will not bog down the system as my various attempts have.
I recognize that this may be too abstract a question, but am hoping that there's some mechanism for just such a use that I'm simply overlooking.
I don't think there's any good way to do a short-circuit in SQL. You can construct a WHERE clause that uses OR to combine the critiera, but doing this generally prevents it from using the indexes. You can use a UNION like this:
SELECT * FROM
(SELECT 1 precedence, table.*
FROM table
WHERE field1 = 'value'
UNION
SELECT 2 precedence, table.*
FROM table
WHERE field2 = 'value'
...
) x
ORDER BY precedence
LIMIT 1
where you replace field1, field2, etc. with the field names from the input array. This will produce the desired results in one query, but it will have to perform all the sub-queries, it won't short-circuit.
The best solution is probably to solve it in the application code. Loop through the fields in the input, and perform a query for just that field. When you get a result, break out of the loop and return it.

How to search either on id or name for certain purchase orders

We would like to filter purchase orders either based on purchase order id (primary key) or name of the purchase order using a single search box.
We used the like parameter to search on the name field, but it doesn't seem to work on the primary key. It works only when we use the equal operator for id(s). But it would be preferable if we can filter purchase orders using like for id(s). How to do this?
create table purchase_orders (
id int(11) primary key,
name varchar(255),
...
)
Option 1
SELECT *
FROM purchase_orders
WHERE id LIKE '%123%'; -- tribute to TemporaryNickName
This is horrible, performance-wise :)
Option 2a
Add a text column which receives a string version of id. Maybe add some triggers to populate it automatically.
Option 2b
Change the type of id column to CHAR or VARCHAR (I believe CHAR should be preferred for a primary key).
In both 2a. and 2b. cases, add an index (maybe a FULLTEXT one) to this column.
I think LIKE should work. I assume that your SQL wasn't correctly written.
Let's assume that you have order name "ABCDEF" then you can find this using the following query structure.
SELECT id FROM purchase_orders WHERE name LIKE '%CD%';
To explain it, % sign means it's a wildcard. As a result this query is going to select any String that contains "CD" inside of it.
According to the table structure, varchar can contain 255 characters. I think this is quite a large string and it's probably going to consume a lot of resources and going to take more time to search something using SQL functions like LIKE. You can always search it by id
WHERE id = something. This is much faster way btw
, but I don't think order id is an user friendly data, instead I would let users to use product name. My recommendation is to use apache Lucene or MySQL's full text search feature (which can improve search performance).
Apache lucene
MySQL Full text search function
These are tools built to search certain pattern or word through list of large strings in much faster way. Many websites use this to build their own mini search engines. I found mysql full text search function requires pretty much no learning curve and straight forward to use =D

SQL query from multiple columns with prepared statement

I want to be able to search and return results across the majority of columns in a mysql table. I considered using a myisam db and a full text search, but decided that because of the performance hit it would be better to stick with innodb. I also briefly considered using something like Solr, but a solution like that seems like overkill in my situation.
I am simply trying to add search functionality to a database of contact information. When the user enters say "Smith" I want the result to return and display all table rows where the first name, last name or even address contains the word Smith etc.
Tell me if this is the wrong approach, but I have settled on using a standard SQL string set up like this:
SELECT * FROM contacts WHERE firstName LIKE ? OR lastName LIKE ? OR email LIKE ?
Of course the actual query string will be considerably longer than this example because it will include most of the columns in the table.
1) I prefer to use prepared statements when querying the database for security, but when I set the sql string up how it is above, the code looks for a separate statement for each ?. How can I just repeat the same "LIKE ?" for each column? Or is there a way to set this up similar to:
SELECT * FROM contacts WHERE firstName, lastName, email LIKE ?
When I initially pass the string into the preparedstatement, I surround it with wildcards (%).
2) Will I be able to search both VARCHAR and INT columns?
EDIT: I ended up using a small portion of the Spring framework so I could be able to use named parameters. Unfortunately JDBC does not have this functionality. This meant including a small portion of the Spring framework in my build path so I could use the jdbc jar. Alternatively I could have passed in my one search string for each parameter in the query string, but this would result is ugly/hard to read code and I didn't want to hack around using the same string which is why I opted for using named parameters.
No you can't do that. You need to do it how you have it in the first query:
SELECT * FROM contacts WHERE firstName LIKE ? OR lastName LIKE ? OR email LIKE ?