mysql postcode search - php

Evening All.
I have a mysql database for a property website. There is a search form where people can enter a location or postcode in the same field.
Part of the SQL is
PostCode LIKE '$Loc%
Put my problem is some people enter a post code like this : "l236yt" and some with a space like this "l23 6yt".
The database contains the postcodes with the space in them so how can make it work with or without the space ??
Any help will be greatly appreciated
thanks baz

Assuming the values in your database are without space, just sanitize the user value to a value without space:
$val = str_replace(' ', '', $val);

You could convert the string into an array of characters, and search for spaces, if you don't find any, you can then proceed to insert a space (the space will always be after the third character in a 6 character postcode, and after the fourth character in a 7 character postcode, so you can use regex to do this quite simply. I believe there are also postcodes which are 5 characters long, and these will be like A1 1AA, so after the second character you'll find the space.
I can't help with precise code due to my lack of knowledge of the language, but good luck!

Expects a valid postcode as input (with or without spaces), and breaks it down into constituent parts as well as returning it neatly formatted with a space in the appropriate place
function parsePostcode($postcode) {
$postcode = preg_replace('/\s*/','',strtoupper($postcode));
$sector = substr($postcode,0,-2);
$outcode = $district = substr($sector,0,-1);
list($area) = sscanf($district,'%[A-Z]');
$incode = substr($postcode,-3);
return array(
'postcode' => $postcode,
'formatted' => $outcode.' '.$incode,
'area' => $area,
'district' => $district,
'sector' => $sector,
'outcode' => $outcode,
'incode' => $incode,
);
}

Your first priority is to remove the LIKE condition from your query. Using LIKE conditions forces MySQL to evaluate every row in your table and is very inefficient. Try to avoid using a LIKE unless absolutely necessary. In order to change this part of your query, you will need to replace it with:
PostCode = "$Loc"
This presents you with two options:
1) sanitise your input. Postcodes follow a well known format so it is possible to convert the value that someone enters into something you expect. You can then search on. As $Loc would match exactly what you have in the database, it would be very fast to find in your database (provided you have indexed the field of course!).
2) overload the database with multiple values to represent the same postcode. This would mean that you would put both "l236yt" and "l23 6yt" in the database and handle them as if they are different values. This also helps when you want to search on just the first part of the postcode, such as "l23", but would only work if you have a one-to-many relationship between postcodes and locations.

Related

Search phone number in database table

I have a phone record tables as
phones(id, number);
It may have values as:-
1, 9801234567
2, 980 1234568
3, 9779801234569
4, 9801234570
5, 977 980 1234 571
If someone search for 980 1234567 (with spaces), I can remove the spaces before running the queries to get the result.
But my problem is when someone search for 9779801234571 with a condition that there is no regular format of number, it must return the last record i.e. 977 980 1234 571. Any idea how to do it efficiently?
Here is a way to do this:
where replace(phonenumber, ' ', '') = replace($phonenumber, ' ', '')
Doing this efficiently is another matter. For that, you would have to format your phone numbers in the database in a canonical format -- say by removing all the spaces in an update statement. Then put the number you are searching for in the same format. The query can then use an index on the column.
It is probably best to clean the phone numbers from whitespace before you write them into the database. You can easily to this using this function:
$string = preg_replace('/\s+/', '', $string);
Maybe you also have to strip out other characters like - or /.
Then you can use a simple WHERE condition without bells and whistles. This will also significantly improve the performance of your SELECT statement since you don't have to do conversions of your data in order to find the right row.
This is assuming that you fill the database yourself of course. If that's not the case, ignore this advise.

Two partial MySQL matches in a concat_ws?

Here's my use case: I'm searching for a person by first and last name, but only type in a partial first and partial last name, how can I create a WHERE clause that catches all possible scenarios?
Example, I type "Joe Smith" and it has a result. I type "Joe" and it has Joe Smith and a few other Joe's. I type "Joe Sm" and it gives me Joe Smith.
I want to be able to type "J Smit" and get Joe Smith, is that possible? Do I need to break the search term on spaces in PHP before doing a LIKE?
Here's what I have so far that works with full matches:
WHERE CONCAT_WS(' ', owner.first_name, owner.last_name)
LIKE '%". $searchTerm ."%'
Any help would be greatly appreciated.
Why don't you do an explode(' ',$input) on your input in PHP and then compare all values of that array in your WHERE clause?
$inputArray = explode(' ',$input);
foreach ($inputArray as $part)
{
$whereArray = "CONCAT_WS(' ',owner.first_name,owner.last_name) LIKE '%$part%'";
}
$where = implode(' AND ',$whereArray);
And then use it like this:
$query = "SELECT * FROM owner WHERE $where";
Please pay attention to security, I didn't do that.
This still doesn't quite do what you want. Because when you want to search for "J Smit" you want the system to be intelligent enough, to search one part, say "J" in the first name column and the other part "Smit" in the last name column. Clearly that's more complex, and the complexity increases with the number of parts to match. There is a solution for that, but you won't like it, it's ugly.
Has anybody got a, not so ugly, solution to this?
It sounds like you do want split the search term into a first and last name component, and then run LIKE comparisons against owner.first_name and owner.last_name separately. Unfortunately, I don't know of native mySQL support for straightforward string splitting.
Splitting in PHP first is certainly an option (the answer from #KIKOSoftware seems to do a good job of that). If you want to try to do it all in mySQL as an alternative, this SO question offers some insight (you will have to modify for your use case, since you're delimiting on white space instead of commas):
How to split the name string in mysql?

SQL LIKE Querying PHP Serialized data with Drupal 7

Using Drupal 7, and I'm trying to get results from the database using the LIKE command but it doesn't recognize my wildcards. I'm not sure if this is even a Drupal issue, or if I'm doing something wrong. Anyways here's an example of the data I'm trying to match, along with my patters
Data to Match
a:2:{i:1;s:2:"17";i:2;s:1:"3";}
My like Queries
$pattern1 = 'a:2:{i:1;s:2:"17";i:2;s:1:"%";}'//works
$pattern2 = 'a:2:{i:1;s:1:"%";i:2;s:1:"3";}'//fails
$result = db_query(
"
SELECT pa.nid, pa.model, pa.combination
FROM {$Product_Adjustments} pa
WHERE pa.combination LIKE :pattern
",
array(
':pattern' => $pattern1
)
);
Additionally, I've tried the '_' wildcard, but that doesn't bring anything up either
Are you sure the pattern is correct? Notice pattern 1, the first string is 2 long, and in pattern 2 you're looking for one that's only 1 long. Are you sure that's right? Are the lengths of the individual pieces of that serialized data predictable enough to even query this way? It seems unlikely, and you'll probably have to store some normalized data instead.

How can I avoid separating the search query to search a MySQL database?

When a user tries to search for people on our system, we've noticed that they often type a full name for their search.
How can we enable full name searching on our database when first name and last name are stored in different columns?
A working but neither fast nor reliable solution would be:
SELECT foo
FROM bar
WHERE CONCAT(firstname, ' ', lastname) = $search_name
(Not sure about MySQL CONCAT syntax atm, you might need to concat twice)
There is a high chance that this won't use any indexes, slowing down the search a lot.
A better solution would be to just split the name:
$names = explode(' ', $search_name);
SELECT foo
FROM bar
WHERE firstname = $names[0]
AND lastname = $names[1]
Even better:
Provide two input boxes and label them first and lastname so users will enter the search correctly.
Even better - If you are using CakePHP 1.3 then simply create a 'full_name' virtual field. Do your query against that field.
http://book.cakephp.org/view/1609/Creating-virtual-fields
http://book.cakephp.org/view/1610/Using-virtual-fields
class User extends AppModel {
...
var $virtualFields = array(
'full_name' => 'CONCAT( User.first_name, " ", User.last_name )'
);
...
}
Since Cake treats virtual fields as regular fields for the primary model you can simply do a find like so.
$User->find( 'all', array(
'conditions' => array(
'User.full_name' => $search_name
...
),
...
);
You could even throw in some MySQL LIKE goodness in the conditions
$User->find( 'all', array(
'conditions' => array(
'User.full_name LIKE' => '%' . $search_name . '%',
...
),
...
);
This syntax means the end-user can put part of the first, part of the last or the whole name into the search field and still get results without worrying about if the code is doing finds on first, last or both.
Your 99% case is probably going to be this:
Split the string on the first whitespace
Assume the first part is the first name, and the 2nd part is the last name
Depending on the number of users in your system, and the... "creativeness" of their names, you may get a few false negatives from this approach.
You could also consider providing separate fields to the user.
You could set up a single fulltext index on first_name and last_name. Then, just include a test for MATCH (first_name, last_name) AGAINST ($query). Or if you have more than just that field to search against, add those to the index as well.
Let SQL do the work for you.
How do you search the people in your DB?
If you have a SQL query you can do it somehow like that:
Separate the search text into parameters. so you have firstname and lastname in 2 separate parameters. (Stackoverflow: How to split a string in php)
then you can use the different parameters to build a query that fits your needs:
select *
from People
where
(firstname LIKE param1 AND lastname LIKE param2) OR
(firstname LIKE param2 AND lastname LIKE param1) OR ...

Using Array "Keys" In a MySQL WHERE Clause

I have a data set that is generated by a Zip Code range search:
$zips:
key -> value
11967 -> 0.5
11951 -> 1.3
The key is the Zip Code (Which I need to query the Database for), and the value is the miles from the user entered zip code. I need to take the key (Zip Code) and search the database, preferably using a MySQL query similar to my current one:
$getlistings = mysql_query("SELECT * FROM stores WHERE zip IN ($zips)");
The other alternative is to change the array somehow in my code. I tried looking in code for where the array is generated originally but I couldn't find it. Any help would be greatly appreciated!! Thanks :)
You could convert the array keys to a SQL-compatible string. For example:
'11967', '11951'
and then use the string in the query.
Since the SQL query doesn't know what a php array is and there's no good way (that I know of) to extract just the keys and surround them in quotes, so this may be your best bet.
EDIT: As Ionut G. Stan wrote (and gave an example for), using the implode and array_map functions will get you there. However, I believe the solution provided will only work if your column definition is numeric. Character columns would require that elements be surrounded by apostrophes in the IN clause.
array_keys should be what you're looking for.
$zip = array_keys($zips); # gives you simple array(11967, 11951);
implode(', ', $zip); # results in: '11967, 11951'
Cannot comment the other answers, so one additional remark from my side. Depending on the country you are in and what you do with the data... In Germany there are ZIP-Codes starting with "0" so you should make sure that you either do not store them as numerical value if you want to compare them to other data (e.g. ZIP <-> geocoord-mappings) or make sure that you convert them to int everywhere and use filtering on the output.
Old ZIP codes had four numbers, new ones have five. So displaying a new ZIP with four numbers because the leading 0 is missing will lead to confusion.
Regarding use of a temporary table i would say it depends on the size of the table and how many zip codes are used in the query.
This should do it:
// array_map sanitizes the data
$zip_codes = implode(', ', array_map('intval', array_keys($zips)));
$getlistings = mysql_query("SELECT * FROM stores WHERE zip IN ($zip_codes)");
For best performance, you should create a temporary table, fill it with your ZIP codes and query like this:
SELECT *
FROM stores
JOIN temptable
ON zip = tempvalue
Of course this will be more efficient only if you ZIP column is indexed.
I just want to throw in that the previous code snippets gave me some syntax errors and the database just spitted out one entry instead of all relevant data. The following snippet worked for me:
implode("','", $zipNumbers);

Categories