Php MySql query by distance and time - mysql

I have this query that I would like to add a time search to.
Here is my working query:
$query = "SELECT *,(((acos(sin((".$lat."*pi()/180)) * sin((Lat*pi()/180)) +
cos((".$lat."*pi()/180)) * cos((Lat*pi()/180)) * cos(((".$lon."- Lon) *
pi()/180))))*180/pi())*60*1.1515) as distance
FROM items
HAVING distance < ".$distance."
ORDER BY distance
LIMIT ".$min." , ".$max."";
I would like to add something like this
WHERE timestamp > ".$somePastDate."
For hours now I have tried all combinations I can think of with no luck. I bet its simple too and I'll be shaking my head. Thanks in advance.

I suggest you use a nested query for this, as follows
SELECT *, big_cosine_law_distance_formula AS distance
FROM (
SELECT *
FROM items
WHERE items.timestamp > ".$somePastDate."
) AS i
HAVING distance < ".$distance."
The inner query will narrow down your items by time, so you don't have to grind out the big distance formula on them all.
You might also consider using a faster bounding-box-based search to narrow down your items spatially, as described here. http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
You can troubleshoot this kind of thing by starting with the inner query.
SELECT *
FROM items
WHERE items.timestamp > ".$somePastDate."
When you're getting reliable results from that query, add the outer one.

Related

Better way of finding results within the radius (Geo)

I am using a query similar to:
$distance = "st_distance_sphere(Point({$lat}, {$lon}), Point({$lat}, {$lon})) * 0.000621371192";
$query = "SELECT table.*, {$distance} as distance
WHERE {$distance} < 10
AND postcode IN ( 'XX12 3XX', 'XX12 3XX', 'XX12 3XX' )
ORDER BY distance asc
LIMIT 100";
The problem kicks in when postcodes contain 100k+ records, this is when the query will take considerable time to return results.
Is there a way of speeding this query up?
Some of the things that I was considering are:
turning lat and long into a POINT data type. https://dev.mysql.com/doc/refman/5.7/en/spatial-type-overview.html
just like with point one but using Elastic Search https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-queries.html

assign sequential number to each set of duplicate records

I've been searching for an easy solution to a pretty trivial problem. I have an huge set of records (~120,000) that I need to screen for duplicates, assign a sequential number to each set of duplicates, like Assign# below:
Eventually, I am trying to achieve this:
I use P1, P2, and P3 fields as a set of sort parameters in query (ascending/descending) to determine the best/top Name for each set of identical NCBI hits.
I tried a lot of things already and my main problem is that access freezes half way through and I don't really know if the script is functional.
FROM [sortquery]
WHERE ((([sortquery].Name) In
(
SELECT TOP 1 [sortquery].Name
FROM [sortquery] AS Dupe
WHERE Dupe.NCBI=[sortquery].NCBI
ORDER BY Dupe.NCBI
)))
ORDER BY [sortquery].NCBI;
I am open to any suggestion and corrections! Thanks for any help =)
The traditional method is to count:
SELECT
*,
(Select Count(*)
From Sortquery As S
Where S.NCBI = Sortquery.NCBI
And S.P1 * 1000 + S.P3 >= Sortquery.P1 * 1000 + Sortquery.P3) As [Assign#]
FROM
[sortquery]
ORDER BY
NCBI Asc,
P1 Desc,
P3 Desc,
[Name] Asc,
[Assign#] Asc

SELECT IF THEN ELSE statement

I want to do query like this
SELECT if(EXISTS(SELECT * FROM application WHERE id_student=1
AND ap_status<>"Wysłano" AND date(app_date) > (SELECT tax_year FROM const_data)),
(SELECT * FROM application WHERE id_student=1 AND ap_status<>"Wysłano" AND date(app_date) > (SELECT tax_year FROM const_data)),
(SELECT * FROM application WHERE id_student=1 AND date(app_date) > (SELECT tax_year FROM const_data)))
But true or false value should contain one column. Is it possible, to make this in other way?
I'm not even sure if what you tried would execute, but this should accomplish what that looks like it is trying to:
SELECT *
FROM application
WHERE id_student=1 AND date(app_date) > (SELECT tax_year FROM const_data)
ORDER BY ap_status="Wysłano"
LIMIT 1
This will fail if const_data has more than one row though.
Edit: Hmm, this answer is not quite right if you expect multiple records. At this point, the best solution is to remove the limit and handle the results in whatever code processes these results. It CAN be done in a single query, but I generally wouldn't recommend it.
Edit2: Sidenote... if app_date is a datetime, you may see performance boosts by removing the use of the DATE() function and instead converting tax_year to a datetime.
Edit3: Last one, I promise.... probably. In the case where it must be done in one query, and results cannot be processed after, this should work.
SELECT *
FROM application
WHERE id_student=1 AND date(app_date) > (SELECT tax_year FROM const_data)
AND IF(EXISTS([that query]), ap_status<>"Wysłano", TRUE)

How can I "order by" only the LIMIT results in a mysql Query?

Hi I need to get the results and apply the order by only in the limited section. You know, when you apply order by you are ordering all the rows, what I want is to sort only the limited section, here is an example:
// all rows
SELECT * FROM users ORDER BY name
// partial 40 rows ordered "globally"
SELECT * FROM users ORDER BY name LIMIT 200,40
The solution is:
// partial 40 rows ordered "locally"
SELECT * FROM (SELECT * FROM users LIMIT 200,40) AS T ORDER BY name
This solution works well but there is a problem: I'm working with a Listview component that needs the TOTAL rows count in the table (using SQL_CALC_FOUND_ROWS). If I use this solution I cannot get this total count, I will get the limited section count (40).
I hope you will give me solution based on the query, for example something like: "ORDER BY LOCALLY"
Since you're using PHP, might as well make things simple, right? It is possible to do this in MySQL only, but why complicate things? (Also, placing less load on the MySQL server is always a good idea)
$result = db_query_function("SELECT SQL_CALC_FOUND_ROWS * FROM `users` LIMIT 200,40");
$users = array();
while($row = db_fetch_function($result)) $users[] = $row;
usort($users,function($a,$b) {return strnatcasecmp($a['name'],$b['name']);});
$totalcount = db_fetch_function(db_query_function("SELECT FOUND_ROWS() AS `count`"));
$totalcount = $totalcount['count'];
Note that I used made-up function names, to show that this is library-agnostic ;) Sub in your chosen functions.

Do I need a temporary table?

I have the following query and I need to add "and distance < 10" to the where clause. Because 'distance' is computed variable it can't be used in where clause. I have tried HAVING instead of where but that breaks the replace part.
I think the answer is to use a temporary table for the distance computation but i can't figure out the syntax as everything I have tried doesn't work.
All help appreciated please. Thanks.
select
Contractor.contractorID,
Contractor.firstName,
Contractor.lastName,
Contractor.emailAddress,
Contractor.nationality,
Contractor.dateOfBirth,
Contractor.address1,
Contractor.address2,
Contractor.city,
Contractor.county,
Contractor.postcode,
Contractor.country,
Contractor.tel,
Contractor.mob,
postcodes.Grid_N Grid_N1,
postcodes.Grid_E Grid_E1,
(select Grid_N from postcodes where pCode='".$postcode."') Grid_N2,
(select Grid_E from postcodes where pCode='".$postcode."') Grid_E2,
( (select sqrt(((Grid_N1-Grid_N2)*(Grid_N1-Grid_N2))+((Grid_E1-Grid_E2)*(Grid_E1-Grid_E2))) ))/1000*0.621371192 as distance
from
Contractor,
postcodes
where
postcodes.Pcode = replace(substring(Contractor.postcode,1,length(Contractor.postcode)-3),'','')
order by
distance asc
In MySQL, you can do this with:
where . . .
having distance < 10
order by distance;
You don't need to put the other conditions in the having clause. Also, your query could benefit from using ANSI standard join syntax (using the on clause, for instance).
SELECT list
, of
, columns
, including
, distance
FROM (
<your existing query>
/* minus ORDER BY clause (needs to be on outermost query */
) As a_subquery
WHERE distance < 10
ORDER
BY some_stuff