Slow query with derived table in FROM clause - sql

I have trying for hours to understand what is going on with this SQL query.
Objective of the query: List all events taking place 6km around a given city.
After all the comments (thanks all) I moved to the query bellow using CTE, removing one useless DISTINCT and using JOINs
BEFORE
SELECT DISTINCT c.id AS cours_id,
GROUP_CONCAT(DISTINCT cl.type) AS type_loc,
GROUP_CONCAT(DISTINCT CONCAT(cl.home,'-',cl.e)) AS home_loc,
MIN(ROUND(pld.distance)) AS distance,
pld.loc2_id AS distance_loc,
GROUP_CONCAT(DISTINCT CONCAT(cl.home,'-',cl.e,'-',ROUND(pld.distance),'-',pld.loc2_id)) AS all_loc
FROM cours2 c,
cours_locations cl,
locations_locations ll,
(SELECT dest.id AS loc2_id, 1000 * 6371.03 * 2 *
ASIN(SQRT( POWER(SIN((orig.latitude - ABS(dest.latitude)) *
PI()/180 / 2),2) + COS(orig.latitude *
PI()/180 ) * COS(ABS(dest.latitude) *
PI()/180) * POWER(SIN((orig.longitude - dest.longitude) *
PI()/180 / 2), 2) )) AS distance
FROM locations orig, locations dest
WHERE orig.id = 14861
AND (dest.type='V' OR dest.type='A')
AND dest.latitude BETWEEN orig.latitude - (6000 / 1000 / 111.045) AND orig.latitude + (6000 / 1000 / 111.045)
AND dest.longitude BETWEEN orig.longitude - (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude))))
AND orig.longitude + (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude))))
HAVING distance <= (6000 + 1000)) pld
WHERE
c.active_today = '1' AND
c.id = cl.cours_id AND
cl.domain = '4' AND
c.subject_id = 404 AND
cl.location_id = ll.rel_loc_id AND
ll.location_id = pld.loc2_id
GROUP BY c.id;
AFTER
WITH location_distances (loc_id, distance) AS (
SELECT dest.id AS loc_id,
1000 * 6371.03 * 2 * ASIN(SQRT( POWER(SIN((orig.latitude - ABS(dest.latitude)) * PI()/180 / 2),2) + COS(orig.latitude * PI()/180 ) * COS(ABS(dest.latitude) * PI()/180) * POWER(SIN((orig.longitude - dest.longitude) * PI()/180 / 2), 2) )) AS distance
FROM locations orig,
locations dest
WHERE orig.id = 14861
AND (dest.type='V' OR dest.type='A')
AND dest.latitude BETWEEN orig.latitude - (6000 / 1000 / 111.045) AND orig.latitude + (6000 / 1000 / 111.045)
AND dest.longitude BETWEEN orig.longitude - (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude)))) AND orig.longitude + (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude))))
HAVING distance < 7000
)
SELECT c.id AS cours_id,
GROUP_CONCAT(DISTINCT cl.type) AS type_loc,
GROUP_CONCAT(DISTINCT CONCAT(cl.home,'-',cl.e)) AS home_loc,
GROUP_CONCAT(DISTINCT CONCAT(cl.home,'-',cl.e,'-',ROUND(pld.distance),'-',pld.loc_id)) AS all_loc
FROM cours2 c
JOIN cours_locations cl ON cl.domain = '4' AND c.id = cl.cours_id
JOIN locations_locations ll ON cl.location_id = ll.location_id AND ll.up_propagation = 0
JOIN location_distances pld ON ll.rel_loc_id = pld.loc_id
WHERE c.active_today = '1'
AND c.subject_id = 404
GROUP BY c.id;
The exec time has not changed (it has even slightly increased): about 15 seconds.
The CTE part by itself runs in 290ms and returns 4 rows. This part is about finding the cities which are 6km distant from the city id 14681.
Yet, strangely:
if I remove the HAVING clause from the CTE, it still runs in 290ms with 4 rows but this time the global query takes 60 seconds instead of 15.
if I add a LIMIT 5 to the CTE, the global query falls to 300ms instead of 15 seconds.
if I add a LIMIT 15 (while the number of rows is 4), it falls to 60 seconds.
These observations can be reproduced with or without CTE, with or without JOINs.
The DB server version is 10.3.27-MariaDB
Table sizes :
cours2 c (list of events) : 15K
cours_locations cl (areas in which events take place) : 45K
locations_locations ll (hierarchy of areas - moving from a region to a city) : 310K
location_distances pld (computed cities 6km around the input city) : 5
Here is the EXPLAIN result :
|id|select_type|table|type|possible_keys|key|key_len|ref|rows|Extra|
|--|--------------|--------------|--------------|-------------|----------------|--------|------------|---------|---------|
|1|PRIMARY|c|ref|ID,cours2_active_today_id,cours2_active_today_domain_display_home_update_id,cours2_active_today_subject_id_id,cours2_active_today_subject_id_domain_lang_id,cours2_active_today_subject_id_lang_publish_end_id,cours2_active_today_lang_priv_loc_MOVE_subject_id_id,cours2_active_today_lang_subject_id_id,cours2_active_today_lang_priv_loc_ADR_subject_id_id,cours2_active_today_lang_priv_loc_WEBCAM_subject_id_id,cours2_subject_id_lang_id_index|cours2_active_today_subject_id_domain_lang_id|5|const,const|179|Using where; Using index; Using filesort|
|1|PRIMARY|cl|ref|PRIMARY,domain,location_id,cours_locations_domain_home_cours_id_index|PRIMARY|27|db.c.id,const|2|Using where; Using index|
|1|PRIMARY|ll|ref|PRIMARY,rel_loc_id|rel_loc_id|4|db.cl.location_id|7|""|
|1|PRIMARY|<derived2>|ref|key0|key0|4|db.ll.location_id|10|""|
|2|DERIVED|orig|const|PRIMARY|PRIMARY|4|const|1|""|
|2|DERIVED|dest|range|locations_type_D_id,locations_type_longitude_latitude|locations_type_longitude_latitude|9||1174|Using index condition; Using where|
EDIT:
Here is a version with 2 CTE running at 300ms total. This is really helping MariaDB to find the proper JOIn optimisation...
WITH
location_distances (loc_id, distance) AS (
SELECT dest.id AS loc_id,
ROUND(1000 * 6371.03 * 2 * ASIN(SQRT( POWER(SIN((orig.latitude - ABS(dest.latitude)) * PI()/180 / 2),2) + COS(orig.latitude * PI()/180 ) * COS(ABS(dest.latitude) * PI()/180) * POWER(SIN((orig.longitude - dest.longitude) * PI()/180 / 2), 2) ))) AS distance
FROM locations orig,
locations dest
WHERE orig.id = 14861
AND (dest.type='V' OR dest.type='A')
AND dest.latitude BETWEEN orig.latitude - (6000 / 1000 / 111.045) AND orig.latitude + (6000 / 1000 / 111.045)
AND dest.longitude BETWEEN orig.longitude - (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude)))) AND orig.longitude + (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude))))
HAVING distance < 7000
),
location_distances_hierarchy (parent_loc_id, loc_id, distance) AS (
SELECT DISTINCT ll.location_id, ld.loc_id, ld.distance
FROM locations_locations ll,
location_distances ld
WHERE ll.rel_loc_id = ld.loc_id
)
SELECT c.id AS cours_id,
GROUP_CONCAT(DISTINCT CONCAT(cl.type,'-',cl.home,'-',cl.e,'-',ldh.distance,'-',ldh.loc_id)) AS all_loc
FROM cours2 c
JOIN cours_locations cl ON cl.domain = '4' AND c.id = cl.cours_id
JOIN location_distances_hierarchy ldh ON cl.location_id = ldh.parent_loc_id
WHERE c.active_today = '1'
AND c.subject_id = 404
GROUP BY c.id;
New EXPLAIN result :
|id|select_type|table|type|possible_keys|key|key_len|ref|rows|Extra|
|1|PRIMARY|ss|ref|PRIMARY,subject_id|PRIMARY|4|const|1|Using index; Using temporary; Using filesort|
|1|PRIMARY|c|ref|ID,cours2_active_today_id,cours2_active_today_domain_display_home_update_id,cours2_active_today_subject_id_id,cours2_active_today_subject_id_domain_lang_id,cours2_active_today_subject_id_lang_publish_end_id,cours2_active_today_lang_priv_loc_MOVE_subject_id_id,cours2_active_today_lang_subject_id_id,cours2_active_today_lang_priv_loc_ADR_subject_id_id,cours2_active_today_lang_priv_loc_WEBCAM_subject_id_id,cours2_subject_id_lang_id_index|cours2_active_today_subject_id_domain_lang_id|5|const,trouver-un-cours.ss.rel_subject_id|30|Using where; Using index|
|1|PRIMARY|caf|eq_ref|PRIMARY|PRIMARY|26|trouver-un-cours.c.id|1|""|
|1|PRIMARY|cl|ref|PRIMARY,domain,location_id,cours_locations_domain_home_cours_id_index|PRIMARY|27|trouver-un-cours.c.id,const|2|Using where; Using index|
|1|PRIMARY|<derived3>|ref|key0|key0|4|trouver-un-cours.cl.location_id|10|Using where|
|3|DERIVED|<derived2>|ALL|||||1174|Using temporary|
|3|DERIVED|ll|ref|rel_loc_id|rel_loc_id|4|ld.loc_id|7|""|
|2|DERIVED|orig|const|PRIMARY|PRIMARY|4|const|1|""|
|2|DERIVED|dest|range|locations_type_D_id,locations_type_longitude_latitude|locations_type_longitude_latitude|9||1174|Using index condition; Using where|

Related

How do i get result?

I need SUM of this result:
SELECT
ROUND(
(invoicesitems.pscost / invoicesitems.inmedunit -
AVG(ISNULL(mainexstock.peacecost,0)) / invoicesitems.inmedunit), 2)
*
CASE WHEN units.unitqty=3 THEN (invoicesitems.bigqty *
invoicesitems.inbigunit * invoicesitems.inmedunit) +
(invoicesitems.medqty * invoicesitems.inmedunit) +
invoicesitems.smallqty
ELSE (invoicesitems.bigqty * invoicesitems.inmedunit)
+ invoicesitems.smallqty
END AS PROFITS
FROM invoicesitems
INNER JOIN mainexstock ON mainexstock.pid = invoicesitems.pid
INNER JOIN units ON units.[uid] = invoicesitems.punits
WHERE invoicesitems.bid = 'B-0480580'
GROUP BY
invoicesitems.pid, invoicesitems.inbigunit,
invoicesitems.inmedunit, invoicesitems.bigqty,
invoicesitems.medqty, invoicesitems.smallqty,
invoicesitems.pscost, units.unitqty
You can use it as a CTE and get the sum. For example:
with a as (
-- your big query here
)
select sum(profits) as profits from a;

Error in query (1241): Operand should contain 1 column(s)

How to fix this error?
This query return a error: Error in query (1241): Operand should contain 1 column(s)
SELECT uti_id, uti_nome FROM utilizador WHERE uti_escola=1 AND uti_id IN
(SELECT DISTINCT ens_utiid_escola, SQRT(POW(69.1 * (uti_latitude - ?), 2) + POW(69.1 * (? - uti_longitude) * COS(uti_latitude / 57.3), 2)) * 1.609344 AS distance
FROM utilizador LEFT OUTER JOIN ensino ON uti_id=ens_utiid WHERE uti_estado=1
AND uti_tipo=1 HAVING distance < ?) ORDER BY uti_nome
The error is coming from this part of the query:
uti_id IN (SELECT DISTINCT ens_utiid_escola, SQRT(POW(69.1 * (uti_latitude - ?), 2) + POW(69.1 * (? - uti_longitude) * COS(uti_latitude / 57.3), 2)) * 1.609344 AS distance
First, you don't need select distinct in an in subquery. Second, the error is because you have two rows. So if you want to use this logic:
uti_id IN (SELECT ens_utiid_escola
FROM . . .
)
Then you need to repeat the expression for distance where you us it.

Calculate walking speed for people

I am using sql-server to calculate human walking speed. I have a table like:
Name time(in min) latitude longitude
Billy 1 x1 y1
Billy 2 x2 y2
...
Jim 1 k1 m1
Jim 2 k2 m2
...
Kate 1 l1 n1
Kate 2 l2 n2
I want to select the people whose average speed in some hour is between 5-7 m/hr. Here is my code to calculate the distance from point A to Point B using Haversin'law.
SELECT Name,
3956 * 2 * ASIN(SQRT( POWER(SIN((orig_lat1 - abs(dest_lat2)) * pi()/180 / 2), 2)+ COS(orig_longitude * pi()/180 ) * COS(abs(dest_lat2) * pi()/180) * POWER(SIN((orig_longitude - dest_longitude) * pi()/180 / 2), 2) ))
AS distance
From Table1
Where distance>5 and distance<7
Where the orig_lat1,dest_lat2, etc are the latitude and longitude of original point and destination. However, I don't know how to track the speed dynamically, the table is updated every minute.
Thanks.
My thought is in the beginning to declare the latitude and longitude (both original and destination) in some certain way. Say:
DECLARE #orig_lat1 blabla
DECLARE #orig_long blabla
The Goal is to check the Average speed in at least an Hour. So if in any hour the people's speed is 5-7 mile/hour. Then print the people's name.
Assuming I understand the question what you really need to do is join table 1 back to itself looking for the people who have traveled a distance that meets your criteria.
I'm going to make an assumption you are looking for people who have traveled at this speed for a certain amount of time. I decided to make it a variable so you could play around with the amount of time you wanted to sample for.
declare #duration int
set #duration = 1
SELECT a.Name
,a.lat
,a.long
,b.lat
,b.long
,3956 * 2 * ASIN(SQRT( POWER(SIN((a.lat - abs(b.lat)) * pi()/180 / 2), 2)
+ COS(a.long * pi()/180 ) * COS(abs(b.long) * pi()/180)
* POWER(SIN((a.long - b.long) * pi()/180 / 2), 2) ))
AS distance
From Table1 a
inner join Table1 b
on a.personID = b.personID
and a.time = b.time + #duration
Where 3956 * 2 * ASIN(SQRT( POWER(SIN((a.lat - abs(b.lat))
* pi()/180 / 2), 2)+ COS(a.long * pi()/180 ) * COS(abs(b.long)
* pi()/180) * POWER(SIN((a.long - b.long) * pi()/180 / 2), 2) ))
between 5 and 7
You'll need to wait until the 61st minute's entry has been added. Then use:
SELECT NAME FROM
(SELECT Name,
3956 * 2 * ASIN(SQRT( POWER(SIN((orig_lat1 - abs(dest_lat2)) * pi()/180 / 2), 2)+ COS(orig_longitude * pi()/180 ) * COS(abs(dest_lat2) * pi()/180) * POWER(SIN((orig_longitude - dest_longitude) * pi()/180 / 2), 2) ))
AS distance
From Table1 Where time=1) F
LEFT OUTER JOIN
(SELECT Name,
3956 * 2 * ASIN(SQRT( POWER(SIN((orig_lat1 - abs(dest_lat2)) * pi()/180 / 2), 2)+ COS(orig_longitude * pi()/180 ) * COS(abs(dest_lat2) * pi()/180) * POWER(SIN((orig_longitude - dest_longitude) * pi()/180 / 2), 2) ))
AS distance
From Table1 where time=61) S
ON F.Name=S.Name
WHERE (F.Distance + S.Distance) BETWEEN 5 AND 7
This assumes that the names are all unique and that the times in the table are recorded in minutes, but you want the final units to be hours.

Need T-SQL Query find all possible ways

create table #sample (
product varchar(100),
Price float
)
insert into #sample values ('Pen',10)
insert into #sample values ('DVD',29)
insert into #sample values ('Pendrive',45)
insert into #sample values ('Mouse',12.5)
insert into #sample values ('TV',49)
select * from #sample
Consider this situation ...
I have 1000$, I want to buy something listed above.
I want to spend the entire amount
So I need a query which gives how much units in all products will cost 1000$
Any help ?
The problem you are referring to is also known as the knapsack problem. There's a range of algorithms you can use to solve this. The most well known is dynamic programming, it requires that the weights are integer numbers, so you'd have to measure in cents. None of them are easy to implement in t-sql.
I actually found a link to someone's implementation in sql server: http://sqlinthewild.co.za/index.php/2011/02/22/and-now-for-a-completely-inappropriate-use-of-sql-server/
Notice the title, they too find it an inappropriate use of a database.
I'd recommend that you solve this in a different language.
It's possible to remove a lot of data by limiting the space for the current item to the money not already spent.
On my home system it takes between 2.6s and 2.8s to run.
On SQLFiddle the first few runs can take more, then it stabilize around 1.8s.
WITH Unit(N) AS (
SELECT N
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) t(N)
), Counter(N) AS (
SELECT u.n + 10*te.n + 100*hu.n
FROM Unit u CROSS JOIN Unit te CROSS JOIN Unit hu
WHERE u.n + 10*te.n + 100*hu.n <= (SELECT 1000 / Min(Price) FROM Sample))
SELECT N INTO #Counter FROM Counter;
WITH Products AS (
SELECT [Pen], [DVD], [PenDrive], [Mouse], [TV]
FROM (SELECT product, price FROM sample) s PIVOT
(MAX(price) FOR product IN ([Pen], [DVD], [PenDrive], [Mouse], [TV])) p
)
SELECT cP.N Pen, cD.N DVD, cPe.N PenDrive, cM.N Mouse
, CAST((1000 - p.pen * cP.N - p.DVD * cD.N
- p.PenDrive * cPe.N - p.Mouse * cM.N) / p.TV as INT) TV
, Money = p.pen * cP.N + p.DVD * cD.N + p.PenDrive * cPe.N
+ p.Mouse * cM.N
+ p.TV * CAST((1000 - p.pen * cP.N - p.DVD * cD.N
- p.PenDrive * cPe.N - p.Mouse * cM.N) / p.TV as INT)
From Products p
LEFT Join #Counter cP ON cP.N <= (1000 / p.Pen)
LEFT Join #Counter cD ON cD.N <= ((1000 - p.pen * cP.N) / p.DVD)
LEFT Join #Counter cPe
ON cPe.N <= ((1000 - p.pen * cP.N - p.DVD * cD.N) / p.PenDrive)
LEFT Join #Counter cM
ON cM.N <= ((1000 - p.pen * cP.N - p.DVD * cD.N
- p.PenDrive * cPe.N) / p.Mouse)
WHERE p.pen * cP.N + p.DVD * cD.N
+ p.PenDrive * cPe.N + p.Mouse * cM.N
+ p.TV * CAST((1000 - p.pen * cP.N - p.DVD * cD.N - p.PenDrive * cPe.N
- p.Mouse * cM.N) / p.TV as INT) = 1000
What's changed
The #Counter is now a temp table, it's calculated only once
The various Product CTEs are gone, substituted by the sample table pivoted
The CROSS JOIN in the Products CTE are gone, they remain in the main select but four less CROSS JOIN is always good
The TOP is gone, the WHERE condition takes care of showing only the perfect solutions
In the main select we have now LEFT JOIN... nope they are still CROSS JOIN, the LEFT JOIN are used because the CROSS JOIN don't have the ON clause used to limit the number of rows
How it works
The products price unpivoted make it possible to get the products price by 'name' (column name).
The FROM block works like 4 indented FOR, where the (1000 - already spent) / price clauses, limit the counters only to the values that will not exceed the 1000$.
The last product is always calculated by difference (how many $ are still unspent / price), dropping a CROSS JOIN completely
SQLFiddle Demo with 1000$ as the total money.
With the data provided there are 3531 solution
Old Answer
If you want to have you server run for all the time of you lunch here is a dumb solution.
Mind you, this solution explore all the space of the problem so the performance will be, at best, crappy.
WITH Base(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1)
, Unit(N) AS (
SELECT Row_Number() Over (ORDER BY (SELECT NULL)) - 1
FROM Base)
, Counter(N) AS (
SELECT u.n + 10*te.n + 100*hu.n + 1000*th.n
FROM Unit u
CROSS JOIN Unit te --tens
CROSS JOIN Unit hu --hundreds
CROSS JOIN Unit th --thousands
WHERE u.n + 10*te.n + 100*hu.n + 1000*th.n <= (SELECT 1000 / Min(Price)
FROM Sample))
, Pens AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'Pen' AND N <= 1000 / Price)
, DVDs AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'DVD' AND N <= 1000 / Price)
, Pendrives AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'Pendrive' AND N <= 1000 / Price)
, Mouses AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'Mouse' AND N <= 1000 / Price)
, TVs AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'TV' AND N <= 1000 / Price
)
SELECT TOP 10
Pen = p.Quantity
, DVD = d.Quantity
, Pendrive = pe.Quantity
, Mouse = m.Quantity
, TV = t.Quantity
, Price = p.Price + d.price + pe.price + m.price + t.price
FROM pens p
CROSS JOIN DVDs d
CROSS JOIN Pendrives pe
CROSS JOIN Mouses m
CROSS JOIN TVs t
WHERE p.Price + d.price + pe.price + m.price + t.price <= 1000
ORDER BY p.Price + d.price + pe.price + m.price + t.price DESC
SQLFiddle Demo with 100$ as the total money (it takes about 2 second to run)
SQLFiddle Demo with 200$ as the total money (it takes about 6 second to run)
A demo with 1000$ will probably result in a time-out
How this work
Base serve as base of Unit.
Unit count from 0 to 9
Counter use Unit to count from 0 to 9999, or the limit imposed from by the cheaper money you want to spend divided by the price of the cheaper item.
Every item has his CTE to calculate the price of that item for any number of times within your capital.
The product CTEs are cross joined to check every combination within the limit of the money.
The TOP 10 is here because there can be more then one combination where the exact amount of money is used.
That just to say that yes you can devise a solution in SQLServer, it's not even that difficult, but that doesn't mean that you should to it.
This is hard coded and has little flexiblity. Took my system 2 minutes to run. But might be helpful, sorry if it isn't. fnGenerate_Numbers is a table function that returns integers within the range of the parameters. Ways to do that.
DECLARE #Max INT,
#Pens money,
#Dvds money,
#Pendrives money,
#Mouses money,
#Tvs money
SELECT #Max = 1000,
#Pens = 10,
#Dvds = 29,
#Pendrives = 45,
#Mouses = 12.5,
#Tvs = 49
;WITH Results AS
(
SELECT p.n pens, d.n dvds, pd.n pendrives, m.n mouses, t.n tvs, tot.cost
FROM fnGenerate_Numbers(0, #Max/#Pens) p -- Pens
CROSS JOIN fnGenerate_Numbers(0, #Max/#Dvds) d -- DVDs
CROSS JOIN fnGenerate_Numbers(0, #Max/#Pendrives) pd -- Pendrives
CROSS JOIN fnGenerate_Numbers(0, #Max/#Mouses) m -- Mouses
CROSS JOIN fnGenerate_Numbers(0, #Max/#Tvs) t -- Tvs
CROSS APPLY (SELECT p.n * #Pens + d.n * #Dvds + pd.n * + #Pendrives + m.n * #Mouses + t.n * #Tvs cost) tot
WHERE tot.cost < #Max
), MaxResults AS
(
SELECT
MAX(pens) pens,
dvds,
pendrives,
mouses,
tvs
FROM Results
GROUP BY
dvds,
pendrives,
mouses,
tvs
)
SELECT mr.*, r.cost FROM MaxResults mr
INNER JOIN Results r ON mr.pens = r.pens
AND mr.dvds = r.dvds
AND mr.pendrives = r.pendrives
AND mr.mouses = r.mouses
AND mr.tvs = r.tvs
ORDER BY cost
If I understand the problem statement correctly, then it's a pretty simple query:
select product, price, floor(1000 / price) as QtyToBuy

Measuring distance between two Lat/Lng points

I am having issues trying to return the closest location to the user via an SQL statement. To test I am using the exact same coordinates, here is what I have:
SQL:
SELECT `companies`.`customerName` ,
(3959 *
ACOS(
COS(
RADIANS(37.367485) * COS(RADIANS(`locations`.`gps_lat`)) *
COS(
RADIANS(`locations`.`gps_lng`) - RADIANS(-77.399994) +
SIN(RADIANS(37.367485)) * SIN(RADIANS(`locations`.`gps_lat`))
)
)
)
)
AS `distance`
FROM `locations`
JOIN `companies` ON `locations`.`co_id`
HAVING `distance` > 25
ORDER BY distance
LIMIT 0 , 10
Results:
| customerName | distance |
| SOME COMPANY | 1914.41747964854 |
locations table values:
gps_lat | gps_lng
37.367485 | -77.399994
I used the example from Google, I checked the formula a couple times and I can't seem to come up with where I went wrong. Any help is appreciated.
EDIT:
Since there seems to be some confusion on me knowing what I am doing:
The > was substituted to yield a result, 1914 is obviously greater then 25.
The application of this is passing user coordinates from an Android app to our web server. The Latitude and Longitude will be pulled from the $_GET values and cross-referenced from companies in our web servers MYSQL database.
The syntax above is just me checking my SQL statement in Mysql Workbench. Obviously I am looking for a zero value result.
I think I have solved this issue by changing from Googles example to the formula provided by "Adventures of an adopted yooper". Using his/her version of the Haversine Formula.
Note: The Google example above is using Haversine as well.
SQL:
SELECT `companies`.`customerName` ,
(2 * (3959 * ATAN2(
SQRT(
POWER(SIN((RADIANS(37.367485 - `locations`.`gps_lat` ) ) / 2 ), 2 ) +
COS(RADIANS(`locations`.`gps_lat`)) *
COS(RADIANS(37.367485 )) *
POWER(SIN((RADIANS(-77.399994 - `locations`.`gps_lng` ) ) / 2 ), 2 )
),
SQRT(1-(
POWER(SIN((RADIANS(37.367485 - `locations`.`gps_lat` ) ) / 2 ), 2 ) +
COS(RADIANS(`locations`.`gps_lat`)) *
COS(RADIANS(37.367485)) *
POWER(SIN((RADIANS(-77.399994 - `locations`.`gps_lng` ) ) / 2 ), 2 )
))
)
))
AS 'distance'
FROM `locations`
JOIN `companies` ON `locations`.`co_id`
HAVING distance < 25
ORDER BY distance
LIMIT 0 , 10
For those curious on my application, Final PHP:
GET Value: ?lat=37.367485&lng=-77.399994
$earth_radius_miles = 3959; // Earth radius in miles
$earth_radius_kilometers = 6371; // Earth radius in kilometers
$result_radius = 10000; // Maximum distance in either miles or kilometers
$get_lat = $_GET["lat"];
$get_lng = $_GET["lng"];
$dbSelect = mysql_query("SELECT `companies`.`customerName`,
(2 * (".$earth_radius_miles." * ATAN2(
SQRT(
POWER(SIN((RADIANS(".$get_lat." - `locations`.`gps_lat` ) ) / 2 ), 2 ) +
COS(RADIANS(`locations`.`gps_lat`)) *
COS(RADIANS(".$get_lat.")) *
POWER(SIN((RADIANS(".$get_lng." - `locations`.`gps_lng` ) ) / 2 ), 2 )
),
SQRT(1-(
POWER(SIN((RADIANS(".$get_lat." - `locations`.`gps_lat` ) ) / 2 ), 2 ) +
COS(RADIANS(`locations`.`gps_lat`)) *
COS(RADIANS(".$get_lat.")) *
POWER(SIN((RADIANS(".$get_lng." - `locations`.`gps_lng` ) ) / 2 ), 2 )
))
)
))
AS 'distance'
FROM `locations`
JOIN `companies` ON `locations`.`co_id`
HAVING distance < ".$result_radius."
ORDER BY distance
LIMIT 0 , 10")
or die(mysql_error());
Results:
[{"customerName":"SOME COMPANY","distance":"0"}]
I have tested these results with other coordinates and seems to be working perfectly. Haven't tested anything with great distance between the two points but my application doesn't require me to do so.
Ashok, you can calculate the distance between two cordinates by using the below formula:
where
R = radius of the earth (6,371 km)
Δlat = |lat2- lat1|
Δlong = |long2- long1|