Slow sql select. Two tables - php

My script is running too slow when searching zipcodes within 30 miles of 90210. It was duplicating some even though there are no dups in the zipcode database too, thus the DISTINCT u.id. Besides selecting only the columns I need, any ideas on how to speed this thing up?
<?php
$zipcode = queryDB("SELECT * FROM zipcodes WHERE zipcode='$location' LIMIT 1", 'a');
$distance = 30;
$sql = "SELECT DISTINCT zipcode, ( 3959 * acos( cos( radians(".$zipcode['lat'].") ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(".$zipcode['lon'].") ) + sin( radians(".$zipcode['lat'].") ) * sin( radians( lat ) ) ) ) AS distance FROM zipcodes HAVING distance <= $distance ORDER BY distance";
$zipcodes = queryDB($sql, 'r');
$sql = '';
while($row = mysqli_fetch_assoc($zipcodes)){
$sql .= "u.category='$category' AND u.zipcode='".$row['zipcode']."' AND u.zipcode = l.zipcode";
if($_GET['photos'] == 'y'){ $sql .= " AND photo_1!=''"; }
$sql .= " OR ";
}
$sql = rtrim($sql, ' OR ');
$profiles = queryDB("SELECT DISTINCT u.id, u.*, l.city, l.state, l.zipcode FROM user_accounts as u, zipcodes as l WHERE $sql ORDER BY date_added DESC", 'r');
while($row = mysqli_fetch_assoc($profiles)){
//this is where i display the profile information
} ?>
UPDATE: The biggest issue was not using a join on my user_accounts select. Here's my updated sql query.
$sql = '';
$zipcodes = new ZipCodesRange($location, $distance);
foreach ($zipcodes->zipCodes as $zipcode){
$sql .= "(u.category='$category' AND u.zipcode='".$zipcode['ZIPCODE']."'";
if($_GET['photos'] == 'y'){ $sql .= " AND photo_1!=''"; }
$sql .= ") OR ";
}
$sql = rtrim($sql, ') OR ');
$sql .= ")";
$profiles = queryDB("SELECT u.id, u.photo_1, u.name, u.business, u.date_added, l.city, l.state, l.zipcode FROM user_accounts as u INNER JOIN zipcodes as l ON u.zipcode = l.zipcode WHERE $sql ORDER BY date_added DESC", 'r');

Per #malias from this other question explaining the haversine formula, here is a document showing different ways to improve the speed of this sql: http://tr.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
The main goal is to limit the zipcodes being searched so that you don't have to calculate the haversine of so many records. The suggestion was to add a WHERE clause to filter the bounds of the latitude and longitude. Table indexes can be used for this rectangular filter, then the haversine can be used to filter it as a sphere.

Related

Mysql + GMAPs - Select from DB and count distance

I have MySQL table with some libraries. I need to make SQL query in PHP, which will select library by ID, and count distance. This is invalid query. Where I'm doing mistake in this query ?
$query = "SELECT *,
( 6371 *
acos(
cos(radians(" . $center_lat . ")) *
cos(radians(lat)) *
cos(radians(lng) - radians(" . $center_lng . ")) +
sin(radians(" . $center_lat . ") ) *
sin(radians( lat ) )
)
) AS distance
FROM `libraries`
WHERE id = ". $id ."
LIMIT 0 , 1";

Select and insert in one query SQL

I have a web page where a user clicks on a map, the latitute and longitude are passed to text fields, and along with some other details the data is submitted to the database. I have a formula for calculating the nearest town, and I have a little sample table of towns with their latitudes and logitudes.
At the moment I can display the nearest town and distance, but I can't get it to send to the database. I think I need to put the two queries together but I don't know how. Please be kind, I know my code is terrible and I have lots of security issues which I'm going to try to address later. Thank you!
<?php
$conn = Connect();
/**
* Use the Haversine Formula to display the 100 closest matches to $origLat, $origLon
* Only search the MySQL table $tableName for matches within a 10 mile ($dist) radius.
*/
$origLat = $conn->real_escape_string($_POST['lat']);
$origLon = $conn->real_escape_string($_POST['lng']);
// This is the maximum distance (in miles) away from $origLat, $origLon in which to search
$dist = 50;
$query = "SELECT Townland, lat, lng, 3956 * 2 *
ASIN(SQRT( POWER(SIN(($origLat - lat)*pi()/180/2),2)
+COS($origLat*pi()/180 )*COS(lat*pi()/180)
*POWER(SIN(($origLon-lng)*pi()/180/2),2)))
as distance FROM townland WHERE
lng between ($origLon-$dist/cos(radians($origLat))*69)
and ($origLon+$dist/cos(radians($origLat))*69)
and lat between ($origLat-($dist/69))
and ($origLat+($dist/69))
having distance < $dist ORDER BY distance limit 1";
$result = mysqli_query($conn, $query) or die(mysql_error());
while($row = mysqli_fetch_assoc($result)) {
echo $row['Townland']." > ".$row['distance']."<BR>";
}
$User_ID = $conn->real_escape_string($_POST['User_ID']);
$Species_Name = $conn->real_escape_string($_POST['Species_Name']);
$No_Of_Birds = $conn->real_escape_string($_POST['No_Of_Birds']);
$newdate = $conn->real_escape_string($_POST['Sighting_Date']);
//Converts date to 'yyyy-mm-dd' acceptable to mysql
$newdate=date('Y-m-d',strtotime($newdate));
$Sighting_Details = $conn->real_escape_string($_POST['Sighting_Details']);
$lat = $conn->real_escape_string($_POST['lat']);
$lng = $conn->real_escape_string($_POST['lng']);
$townland = $row['Townland'];
$query = "INSERT into sighting
(User_ID, Species_Name, No_Of_Birds,
Sighting_Date, Sighting_Details,
lat, lng, Townland)
VALUES('" . $User_ID . "','" . $Species_Name . "','" . $No_Of_Birds . "','"
. $newdate . "','" . $Sighting_Details . "','"
. $lat . "','" . $lng . "','" . $townland . "')";
$success = $conn->query($query);
if (!$success) {
die("Couldn't enter data: ".$conn->error);
}
header("Location: ../View/thankYouSubmitSighting.php");
$conn->close();
exit();
while($row = mysqli_fetch_assoc($result)) {
echo $row['Townland']." > ".$row['distance']."<BR>";
}
mysqli_close($conn);
?>
If you need insert the result from query in a table eg: my_table
could be you need an insert select query ( a single sql command composed by two part )
"INSERT into my_table (Townland, lat, lng, distance)
SELECT Townland, lat, lng, 3956 * 2 *
ASIN(SQRT( POWER(SIN(($origLat - lat)*pi()/180/2),2)
+COS($origLat*pi()/180 )*COS(lat*pi()/180)
*POWER(SIN(($origLon-lng)*pi()/180/2),2)))
as distance FROM townland WHERE
lng between ($origLon-$dist/cos(radians($origLat))*69)
and ($origLon+$dist/cos(radians($origLat))*69)
and lat between ($origLat-($dist/69))
and ($origLat+($dist/69))
having distance < $dist ORDER BY distance limit 1";

How to write a mysql query that selects all stores (latitudes and longitudes) ORDER BY nearer to your position

I am making a site in which I have to show all stores on a list sorted in a way that the nearer stores, to the users current location, come first and far ones come later.
I need a MySQL query that selects all stores from database ORDER BY users current position.
I have searched on Google a lot but could not find any query that matches my case.
You can try this:
SELECT temp1.* FROM (SELECT s.shop_id, s.shop_name, s.address, 3959 * ACOS( COS( RADIANS( 40.7127 ) ) * COS( RADIANS( s.latitude ) ) * COS( RADIANS( s.longitude ) - RADIANS( 74.0059 ) ) + SIN( RADIANS( 40.7127 ) ) * SIN( RADIANS( s.latitude ) ) ) AS distance FROM shop s) as temp1 WHERE temp1.distance <= 20 ORDER BY temp1.distance
it will give shop listing within 20 miles.
40.7127 is latitude and 74.0059 is longitude
I think that you'll find that you'll just need to get all the stores from your database (or potentially filtered by something like state to make for a smaller data-set) and then let your application handle ordering them by distance.
You should go with the ip address of the stores with the ip helper to find (host,country,town,region) of the store and map them so that any can find nearby stores. you can find these from ip
<?php
require_once("userip/ip.codehelper.io.php");
require_once("userip/php_fast_cache.php");
$_ip = new ip_codehelper();
$real_client_ip_address = $_ip->getRealIP();
$visitor_location = $_ip->getLocation($real_client_ip_address);
$guest_ip = $visitor_location['IP'];
$guest_country = $visitor_location['CountryName'];
$guest_city = $visitor_location['CityName'];
$guest_state = $visitor_location['RegionName'];
echo "IP Address: ". $guest_ip. "<br/>";
echo "Country: ". $guest_country. "<br/>";
echo "State: ". $guest_state. "<br/>";
echo "City: ". $guest_city. "<br/>";
?>
You can also find
$ip = $visitor_location['IP'];
$Continent_Code = $$$visitor_location['ContinentCode'];
$Continent_Name = $visitor_location['ContinentName'];
$Country_Code2 = $visitor_location['CountryCode2'];
$Country_Code3 = $visitor_location['CountryCode3'];
$Country = $visitor_location['Country'];
$Country_Name = $visitor_location['CountryName'];
$State_Name = $visitor_location['RegionName'];
$City_Name = $visitor_location['CityName'];
$City_Latitude = $visitor_location['CityLatitude'];
$City_Longitude = $visitor_location['CityLongitude'];
$Country_Latitude = $visitor_location['CountryLatitude'];
$Country_Longitude = $visitor_location['CountryLongitude'];
$Country_Longitude = $visitor_location['CountryLongitude'];
$LocalTimeZone = $visitor_location['LocalTimeZone'];
$Calling_Code = $visitor_location['CallingCode'];
$Population = $visitor_location['Population'];
$Area_SqKm = $visitor_location['AreaSqKm'];
$Capital = $visitor_location['Capital'];
$Electrical = $visitor_location['Electrical'];
$Languages = $visitor_location['Languages'];
$Currency = $visitor_location['Currency'];
$Flag = $visitor_location['Currency'];

PHP/mysql Missing Row on query

I am running the following query in a PHP script:
$radius_query = "SELECT zip, ( 3959 * acos( cos( radians(" . $latitude .") ) * cos( radians( lat ) ) * cos( radians( lng ) - radians( " . $longitude . ") ) + sin( radians(" . $latitude .") ) * sin( radians( lat ) ) ) ) AS distance
FROM ZIP HAVING distance < ". $radius;
$radius_result = $db_zipcode->query($radius_query);
if (!$radius_result){
echo 'Could not run query: ' . mysql_error();
exit;
}
$row = $radius_result->fetch(PDO::FETCH_ASSOC);
While($row = $radius_result->fetch(PDO::FETCH_ASSOC))
{
$zip[] = $row['zip'];
}
foreach ($zip as $zip_display){
echo $zip_display . ", " . '';
}
The query itself works with one minor error - it does not return the first row of data. It should return a list of zip codes similar to this:
40324, 40339, 40340, 40347, 40356...
This is the result I get when I run the same query in phpMyAdmin.
However when I run the query above in a PHP script, the result set starts with 40339, and then includes all the remaining zip codes. Its not a mileage issue (i.e. the missing zip is well within the radius - as it shows up when I run the query in phpMyAdmin).
What am I doing wrong in the PHP query to miss that first row of data? Its not just a display problem because when I insert the query results in a DB, it is also missing the first row.
Thanks!
Problem is in the first fetch, they take your first row...
Run this without
$row = $radius_result->fetch(PDO::FETCH_ASSOC);
I had the same problem about a day ago.
There's no need to do an implicit loop in the PHP, try
$zip = $radius_result->fetchAll(PDO::FETCH_ASSOC);

Store values of SQL query in variables

I have a SELECT SQL statement that returns a Latitude, Longitude and Distance as result (using the Haversine formula). Now I need to store these values as variables within the PHP script as I need to do further processing on the results. Can anyone tell me how I can do this?
Here is my SQL Statement
UPDATED
$query = $db->prepare("SELECT `Latitude`, `Longitude`, (6378160 * acos( cos( radians(:latitude) ) * cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians(:longitude) ) + sin( radians(:latitude) ) * sin( radians( Latitude ) ) ) ) AS distance FROM `mytable` HAVING distance < :radius ORDER BY distance;");
// Assign parameters
$query->bindParam(':latitude',$newLatitude);
$query->bindParam(':longitude',$newLongitude);
$query->bindParam(':radius',$radius);
//Execute query
$query->execute();
$q = $db->query($query);
$q->setFetchMode(PDO::FETCH_ASSOC);
// fetch
while($r = $q->fetch()){
$eLatitude = $r->Latitude;
$eLongitude = $r>Longitude;
$distance = $r->Distance;
}
I updated my php code above. Is this code's syntax correct? Or the way I'm doing it is all wrong?
PDO::FETCH_ASSOC: returns an array indexed by column name Manual
while($r = $q->fetch()){
echo $r['Latitude']. "\n"; //Or do what ever instead of echo
echo $r['Longitude']. "\n";
echo $r['Distance']. "\n";
}
ie XML
while($r = $q->fetch()){
$node = $dom->createElement("marker");
$newnode->setAttribute("lat", $r['Latitude']);
$newnode->setAttribute("lng", $r['Longitude']);
$newnode->setAttribute("distance", $r['Distance']);
}
ie JSON
while($r = $q->fetch()){
data[] = $r;
}
you can use
$queryresult = mysql_query("SELECT `Latitude`, `Longitude`, (6378160 * acos( cos( radians(:latitude) ) * cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians(:longitude) ) + sin( radians(:latitude) ) * sin( radians( Latitude ) ) ) ) AS distance FROM `mytable` HAVING distance < :radius ORDER BY distance;");
if (!$queryresult) {
echo 'Error ' . mysql_error();
exit;
}
$resultrow = mysql_fetch_row($queryresult);
echo $resultrow[0]; // this will be latitude
echo $resultrow[1]; // this will be longitude

Categories