Applying a function to an aliased field - mysql

I have a query that creates two aliased files like this:
(CASE WHEN `venueID` > 0 THEN `venueLongitude` ELSE `specialLongitude` END) AS `longitude`
(CASE WHEN `venueID` > 0 THEN `venueLatitude` ELSE `specialLatitude` END) AS `latitude`
which determine which longitude and latitude to use in a certain query (there are numerous JOIN's in the table, but for the sake of this example, the only JOIN is from the Gigs table performing a NATURAL LEFT JOIN to a table called Venues.
That bit works fine and returns the venue co-ordinates I am interested in doing a distance calculation that I can then order by.
The distance field looks like this:
((ACOS(SIN($lat * PI() / 180) * SIN(`latitude` * PI() / 180) + COS($lat * PI() / 180) * COS(`latitude` * PI() / 180) * COS(($lon - `longitude`) * PI() / 180)) * 180 / PI()) * 60 * 1.1515)
But when I run the query, I get the error Unknown column 'latitude' in 'field list'. Does anyone know how I can perform such a lookup.
p.s.
Although I know I can use geospatial queries, I would like to know the answer in a way that could later be used for further queries, i.e.
SELECT *,(CASE WHEN `a` > 0 THEN `a` ELSE `b` END) AS `x`,(`x` MOD 3) AS `y` FROM `Foo`

2 ways :
a) replace the aliases with SQL
b) Encapsulate your first query by another one like this :
SELECT
t.latitude,
t.longitude,
((ACOS(SIN($lat * PI() / 180) * SIN(t.latitude * PI() / 180) + COS($lat * PI() / 180) * COS(latitude * PI() / 180) * COS(($lon - t.longitude) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) as distance
FROM (
SELECT ((CASE WHEN `venueID` > 0 THEN `venueLongitude` ELSE `specialLongitude` END) AS `longitude`, (CASE WHEN `venueID` > 0 THEN `venueLatitude` ELSE `specialLatitude` END) AS `latitude` FROM Gigs left join Venues on ....
) as t

If you want to SELECT the distance, then I believe you need to re-declare the case for the value. AKA: Rather than using SELECT col alias1, alias1 + 1 alias2 you have to do SELECT col alias1, col + 1 alias2. Take a look at this MySQL documentation on Where you can use column aliases.
If you just want to GROUP BY the distance, then you can use your aliases in your distance function. However, if you want to SELECT the distance, you need to replace the alias usage in your distance function with the evaluated CASE code.
ORDER BY using aliases:
SELECT
(CASE WHEN `venueID` > 0 THEN `venueLongitude` ELSE `specialLongitude` END) longitude,
(CASE WHEN `venueID` > 0 THEN `venueLatitude` ELSE `specialLatitude` END) latitude
FROM table_name
ORDER BY ((ACOS(SIN(latitude * PI() / 180) * SIN(latitude * PI() / 180) + COS(latitude * PI() / 180) * COS(latitude * PI() / 180) * COS(longitude * PI() / 180)) * 180 / PI()) * 60 * 1.1515)
SELECT cannot use the aliases (it gets messy!):
SELECT
(CASE WHEN `venueID` > 0 THEN `venueLongitude` ELSE `specialLongitude` END) longitude,
(CASE WHEN `venueID` > 0 THEN `venueLatitude` ELSE `specialLatitude` END) latitude,
(
((ACOS(SIN((CASE WHEN `venueID` > 0 THEN `venueLatitude` ELSE `specialLatitude` END) * PI() / 180) * SIN((CASE WHEN `venueID` > 0 THEN `venueLatitude` ELSE `specialLatitude` END) * PI() / 180) + COS((CASE WHEN `venueID` > 0 THEN `venueLatitude` ELSE `specialLatitude` END) * PI() / 180) * COS((CASE WHEN `venueID` > 0 THEN `venueLatitude` ELSE `specialLatitude` END) * PI() / 180) * COS((CASE WHEN `venueID` > 0 THEN `venueLongitude` ELSE `specialLongitude` END) * PI() / 180)) * 180 / PI()) * 60 * 1.1515)
) distance
FROM table_name
ORDER BY distance

Related

Distance in Kilometer using MySql in PHPmyadmin

I am using PHPmyadmin for mysql.
I want to find the distance between 2 latitude and longitude from a table.
I am using the following query to find the distance.
But it returns in miles . How can get the distance in kilometer
Select #dist:=((ACOS(SIN(1.3903496 * PI() / 180) * SIN(latitude * PI() / 180) + COS(1.3903496 * PI() / 180) * COS(latitude * PI() / 180) * COS((103.8846613- longitude) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance from tabl_name
You can try the following SQL :
SELECT (((acos(sin((".$latitude."*pi()/180)) * sin((`Latitude`*pi()/180))+cos((".$latitude."*pi()/180)) * cos((`Latitude`*pi()/180)) * cos(((".$longitude."- `Longitude`)*pi()/180))))*180/pi())*60*1.1515*1.609344) as distance
FROM `MyTable`
You can directly convert the result of the query to kilometres in the query itself.
Select #dist:=((ACOS(SIN(1.3903496 * PI() / 180) * SIN(latitude * PI() / 180) + COS(1.3903496 * PI() / 180) * COS(latitude * PI() / 180) * COS((103.8846613- longitude) * PI() / 180)) * 180 / PI()) * 60 * 1.1515)*1.609344 AS distance from tabl_name

MySQL using `IN` while having multiple values

I am trying to count of coupons sold by each store from the list of stores within 20 miles range. I know the following syntax will work if there is only 1 store.
SELECT sum(couponscount) as count where restaurant IN (SELECT storename where bhal bhal bhal and output is one value)
What is I the IN (SELECTstorenamewhere bhal bhal bhal and output is multiple values) will return multiple values?
Like in my case the complete SQL is like and its not working
SELECT sum(couponscount) as count FROM `coupons` having `restaurant` IN (SELECT `storename`, ((ACOS(SIN(-27.561264299999998 * PI()/180) * SIN(latitude * PI()/180) + COS(-27.561264299999998 * PI()/180) * COS(latitude * PI()/180) * COS((153.07304890000003 – longitude) * PI()/180)) *180 / PI( )) *60 * 1.1515) AS `distance` FROM `stores` WHERE `status`=’active’ HAVING `distance` <=20)
Is there anyway to make it working?
SELECT sum(couponscount) AS COUNT,restaurant
FROM `coupons`
WHERE `restaurant` IN
(SELECT `storename`
FROM `stores`
WHERE `status`='active'
AND
((ACOS(SIN(-27.561264299999998 * PI()/180) * SIN(latitude * PI()/180) + COS(-27.561264299999998 * PI()/180) * COS(latitude * PI()/180) * COS((153.07304890000003 – longitude) * PI()/180)) *180 / PI()) *60 * 1.1515) <=20)
GROUP BY restaurant
Also use proper quotes for active.
Presumably, you want to get the count of coupons from stores within a distance of 20. Moving the having condition to a where clause should do what you want:
SELECT sum(couponscount) as count
FROM `coupons`
WHERE `restaurant` IN (SELECT `storename`
FROM `stores`
WHERE `status` = 'active' AND
((ACOS(SIN(-27.561264299999998 * PI()/180) * SIN(latitude * PI()/180) + COS(-27.561264299999998 * PI()/180) * COS(latitude * PI()/180) * COS((153.07304890000003 – longitude) * PI()/180)) *180 / PI( )) *60 * 1.1515) <= 20
);
You had a major syntax problem because your subquery returned two columns. When you use a subquery with in, you can only return one column, in this case, storename. I moved the code for the distance calculation to the where clause. No having clause is needed either in the subquery or the outer query.

MySQL Search An Item and all objects in distance

I have a query that searches for the all rows in some distance according to geo coordinates:
SELECT LOCATIONS.*,
( ( ACOS(
SIN(" . $location->Lat . " * PI() / 180)
* SIN(LAT * PI() / 180)
+
COS(" . $location->Lat . " * PI() / 180)
* COS(LAT * PI() / 180)
* COS( ( " . $location->Lng . " - LNG ) * PI() / 180)
)
* 180 / PI()
)
* 60 * 1.1515
) AS DISTANCE
FROM LOCATIONS
HAVING DISTANCE <= 100
ORDER BY DISTANCE ASC
I'm wondering if it's possible to search for the first record of the table and then treat it as a base - use its lat and lng coordinates?
I was trying achieving this with subquery - but with no luck.
Can anybody give me a suggestion?
Put the "base" record by itself into a temporary table using CREATE TEMPORARY TABLE AS SELECT ... LIMIT 1. Then join against the temporary table from the query above.
See http://dev.mysql.com/doc/refman/5.0/en/create-table-select.html

SQL query issue - Unknown column error

I'm trying to select db records based on wordpress custom fields that hold lat and long and have hit a brick wall on this query. Anyone see anything obvious that i'm overlooking?
Thanks!
WordPress database error: [Unknown column 'latitude.meta_value' in 'field list']
SELECT p.ID, p.post_title, (( ACOS( SIN( 39.1749 * PI() / 180 )
* SIN( `latitude.meta_value` * PI() / 180 )
+ COS( 39.1749 * PI() / 180 )
* COS( `latitude.meta_value` * PI() / 180 )
* COS(( -94.5804 - `longitude.meta_value` ) * PI() / 180 )) * 180 / PI() ) * 60 * 1.1515 ) AS distance
FROM wp_posts p
LEFT JOIN wp_postmeta latitude ON latitude.post_id = p.ID AND latitude.meta_key = 'neighborly_issue_lat'
LEFT JOIN wp_postmeta longitude ON longitude.post_id = p.ID AND longitude.meta_key = 'neighborly_issue_lng' HAVING distance < 10;
Yours commas are wrong, try with :
SELECT p.ID, p.post_title, (( ACOS( SIN( 39.1749 * PI() / 180 )
* SIN( `latitude`.`meta_value` * PI() / 180 )
+ COS( 39.1749 * PI() / 180 )
* COS( `latitude`.`meta_value` * PI() / 180 )
* COS(( -94.5804 - `longitude`.`meta_value` ) * PI() / 180 )) * 180 / PI() ) * 60 * 1.1515 ) AS distance

Arel subselects with ActiveRecord?

I'm using a slightly-modified version of the geocoded gem which returns this query when I call near on my model (calling Deal.near(southwest), where southwest is an array of geo coordinates):
SELECT
deals.*,
3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180 / 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat * PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() / 180 / 2), 2) )) AS distance,
CAST(DEGREES(ATAN2( RADIANS(addresses.lng - -122.42336332798004), RADIANS(addresses.lat - 37.772476604436974))) + 360 AS decimal) % 360 AS bearing
FROM "deals"
INNER JOIN "companies" ON "companies"."id" = "deals"."company_id"
INNER JOIN "addresses" ON "addresses"."addressable_id" = "companies"."id" AND "addresses"."addressable_type" = 'Company'
WHERE (
addresses.lat BETWEEN 37.483013038215276 AND 38.06194017065867
AND addresses.lng BETWEEN -122.78956461309022 AND -122.05716204286986
)
GROUP BY
deals.id,
deals.created_at,
deals.updated_at,
deals.active,
deals.company_id,
deals.title,
deals.limitations,
deals.redemption_count,
addresses.lat,
addresses.lng
HAVING 3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180 / 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat * PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() / 180 / 2), 2) )) <= 20
ORDER BY 3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180 / 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat * PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() / 180 / 2), 2) )) ASC
My issue is that this will return multiple Deal records if that Deal's company has multiple Addresses, which I don't want.
In MySQL, I could just omit address.lat, address.lng in the GROUP_BY clause and it will properly group the records, but I can't do this in PostgreSQL.
I know I could wrap the whole query above in another SELECT and GROUP_BY, like this:
SELECT
id, created_at, updated_at, active, title, punches_to_complete, company_id, description, lat, lng, MIN(distance), bearing
FROM ( ... ) AS t
GROUP BY company_id
... where the ellipsis is the query from above. That (I believe) should get me the desired result in both MySQL and PostgreSQL.
The only problem is that I have no idea how to write this in ARel!
I had tried the following, a la this tip from the ARel guru, but I couldn't really make it work quite right (calling to_sql as the OP had said fixed his issue escapes the quotes, which freaks PostgreSQL out).
Can anyone help me with this???
UPDATE:
I've managed to get this done with an additional scope, like so:
scope :nearest, lambda { |coords|
subquery = "(#{Deal.near(coords).to_sql}) AS t1"
columns = Deal.columns.map{ |c| c.name }.join(',')
Deal.select(columns)
.select('MIN(distance) AS distance')
.from(subquery)
.group(columns)
.order('distance ASC')
}
However, this totally breaks chainability, as now I cannot call something like current_user.deals.nearest(coords), since that tags on an additional WHERE deals.user_id = 1 to the query outside of the subselect. I tried compensating for this by moving this logic into a class method and blanking the wheres clause on the SelectManager manually, like this:
def self.nearest(coords)
subquery = "(#{Deal.near(coords).to_sql}) AS t1"
columns = Deal.columns.map{ |c| c.name }.join(',')
query = Deal.select(columns)
.select('MIN(distance) AS distance')
.from(subquery)
.group(columns)
.order('distance ASC')
query.arel.ast.cores[0].wheres = []
query
end
... but that doesn't seem to work either: the additional WHERE clause is still appended:
Failure/Error:
#user.deals.nearest(southwest).first.distance.to_f.round(2).should ==
ActiveRecord::StatementInvalid:
Mysql2::Error: Unknown column 'deals.user_id' in 'where
clause': SELECT id,created_at,updated_at,user_id,company_id,
MIN(distance) AS distance FROM (SELECT deals.*, 3958.755864232 * 2 *
ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180
/ 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat *
PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() /
180 / 2), 2) )) AS distance, CAST(DEGREES(ATAN2( RADIANS(addresses.lng
- -122.42336332798004), RADIANS(addresses.lat - 37.772476604436974)))
+ 360 AS decimal) % 360 AS bearing FROM deals INNER JOIN companies
ON companies.id = deals.company_id INNER JOIN addresses ON
addresses.addressable_id = companies.id AND
addresses.addressable_type = 'Company' WHERE deals.user_id =
26 AND (addresses.lat BETWEEN 37.483013038215276 AND 38.06194017065867
AND addresses.lng BETWEEN -122.78956461309022 AND -122.05716204286986)
GROUP BY
deals.id,deals.created_at,deals.updated_at,deals.user_id,deals.company_id,
addresses.lat, addresses.lng HAVING 3958.755864232 * 2 *
ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180
/ 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat *
PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() /
180 / 2), 2) )) <= 20 ORDER BY 3958.755864232 * 2 *
ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180
/ 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat *
PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() /
180 / 2), 2) )) ASC) AS t1 WHERE deals.user_id = 26 GROUP BY
id,created_at,updated_at,user_id,company_id ORDER BY distance ASC
LIMIT 1
Is what I'm trying to do even possible with ARel? The additional scopes above feel really dirty to me (parsing the subquery to raw SQL? I thought ARel was supposed to make it so I never did that!)
Related question: Can ARel formulate cross-db queries for CTEs (Common Table Expressions)?