SQL Server AS statement aliased column within WHERE statement - sql

I want to execute a query in which I rename one of the columns using the 'AS' statement and reuse that aliased column name within the 'WHERE' statement. Below is an example:
SELECT lat AS latitude
FROM poi_table
WHERE latitude < 500
The problem here is that SQL Server does not like this query because of the WHERE clause and the AS statement name being referenced in the WHERE clause. Can anyone explain why this is happening and what I can do to remedy my situation?
Suppose I were to have a formula that I have aliased in the SELECT portion of the query, how do I tackle that?
SELECT *,
( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) )
AS distance
FROM poi_table
WHERE distance < 500;

SQL doesn't typically allow you to reference column aliases in WHERE, GROUP BY or HAVING clauses. MySQL does support referencing column aliases in the GROUP BY and HAVING, but I stress that it will cause problems when porting such queries to other databases.
When in doubt, use the actual column name:
SELECT t.lat AS latitude
FROM poi_table t
WHERE t.lat < 500
I added a table alias to make it easier to see what is an actual column vs alias.
Update
A computed column, like the one you see here:
SELECT *,
( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) ) AS distance
FROM poi_table
WHERE distance < 500;
...doesn't change that you can not reference a column alias in the WHERE clause. For that query to work, you'd have to use:
SELECT *,
( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) ) AS distance
FROM poi_table
WHERE ( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) ) < 500;
Be aware that using a function on a column (IE: RADIANS(lat)) will render an index useless, if one exists on the column.

SQL Server is tuned to apply the filters before it applies aliases (because that usually produces faster results).
You could do a nested select statement. Example:
SELECT Latitude FROM
(
SELECT Lat AS Latitude FROM poi_table
) A
WHERE Latitude < 500
I realize this may not be what you are looking for, because it makes your queries much more wordy.
A more succinct approach would be to make a view that wraps your underlying table:
CREATE VIEW vPoi_Table AS
SELECT Lat AS Latitude FROM poi_table
Then you could say:
SELECT Latitude FROM vPoi_Table WHERE Latitude < 500

I am not sure why you cannot use "lat" but, if you must you can rename the columns in a derived table.
select latitude from (SELECT lat AS latitude FROM poi_table) p where latitude < 500

This would work on your edited question !
SELECT * FROM (SELECT <Column_List>,
( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) )
AS distance
FROM poi_table) TMP
WHERE distance < 500;

Logical Processing Order of the SELECT statement
The following steps show the logical processing order, or binding
order, for a SELECT statement. This order determines when the objects
defined in one step are made available to the clauses in subsequent
steps. For example, if the query processor can bind to (access) the
tables or views defined in the FROM clause, these objects and their
columns are made available to all subsequent steps. Conversely,
because the SELECT clause is step 8, any column aliases or derived
columns defined in that clause cannot be referenced by preceding
clauses. However, they can be referenced by subsequent clauses such as
the ORDER BY clause. Note that the actual physical execution of the
statement is determined by the query processor and the order may vary
from this list.
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
Source: http://msdn.microsoft.com/en-us/library/ms189499%28v=sql.110%29.aspx

Both accepted answer and Logical Processing Order explain why you could not do what you proposed.
Possible solution:
use derived table (cte/subquery)
use expression in WHERE
create view/computed column
From SQL Server 2008 you could use APPLY operator combined with Table valued Constructor:
SELECT *, s.distance
FROM poi_table
CROSS APPLY (VALUES(6371*1000*acos(cos(radians(42.3936868308))*cos(radians(lat))*cos(radians(lon)-radians(-72.5277256966))+sin(radians(42.3936868308))*sin(radians(lat))))) AS s(distance)
WHERE distance < 500;
LiveDemo

I am not sure why you cannot use "lat" but, if you must you can rename the columns in a derived table.
select a.latitude from (SELECT lat AS latitude FROM poi_table) a where latitude < 500

Related

column "distance" does not exist

I am trying to run a query that finds the places near my location, but every time I try to run it, it returns the following error:
column "distance" does not exist
If I remove distance and only leave up to the FROM posts, it returns the post id and the distance column.
But if I leave it in the original way, it returns the error.
SELECT id,
( 3959 * acos( cos( radians(-32.63) ) * cos( radians( latitude ) ) *
cos( radians( longitude ) - radians(-71.42) ) + sin( radians(-32.63) ) *
sin( radians( latitude ) ) ) ) AS distance
FROM posts
HAVING distance < 25
ORDER BY distance;
You can't use a column alias in other places in the same query. You'll have to either repeat the entire expression in your HAVING and ORDER BY or use your existing query as a sub-query and apply the HAVING and ORDER BY to the outer query, or use a CTE if your RDBMS supports it.
SELECT id, distance FROM
(SELECT id, ( 3959 * acos( cos( radians(-32.63) ) * cos( radians( latitude ) ) *
cos( radians( longitude ) - radians(-71.42) ) + sin( radians(-32.63) ) *
sin( radians( latitude ) ) ) ) AS distance FROM posts) p
WHERE distance < 25 ORDER BY distance;
you need to put CTA to order it by distance

Invalid column name on Having in SQL Server

I have quick question why I can't use having keyword on distance? I need somehow to check is distance < 20 for example
SELECT
Id, Lat, Lng,
(6367 * acos( cos( radians(45.444) )
* cos( radians( Lat ) ) * cos( radians( Lng ) - radians(158.554) )
+ sin( radians(4545) ) * sin( radians( Lat ) ) ) ) AS distance
FROM
Posts
HAVING
distance < 15 // Invalid column Name
ORDER BY
distance
I might suggest outer apply for this purpose:
SELECT p.Id, p.Lat, p.Lng, d.distance
FROM Posts p OUTER APPLY
(SELECT (6367 * acos( cos( radians(45.444) )
* cos( radians( p.Lat ) ) * cos( radians( p.Lng ) - radians(158.554) )
+ sin( radians(4545) ) * sin( radians( p.Lat ) ) ) ) AS distance
) d
FROM Posts p
WHERE d.distance < 15
ORDER BY distance;
The use of HAVING as a substitute for WHERE is an extension for MySQL. In other databases, you can use a subquery, CTE, or lateral join (which is the technical name for what APPLY does). In this case, I think the lateral join is convenient, because it separates the logic for this quite complicated formula.
Try this
SELECT *
FROM
(SELECT
Id, Lat, Lng,
(6367 * acos(cos(radians(45.444)) * cos(radians(Lat)) *
cos(radians(Lng) - radians(158.554)) + sin(radians(4545)) *
sin(radians(Lat)))) AS distance
FROM Posts) p
WHERE
p.distance < 15
ORDER BY
p.distance
YOu can use brackets to dictate you mean a field name not a pereserved keyword:
SELECT Id,Lat,Lng,(6367 * acos( cos( radians(45.444) )
* cos( radians( Lat ) ) * cos( radians( Lng ) - radians(158.554) )
+ sin( radians(4545) ) * sin( radians( Lat ) ) ) ) AS distance FROM Posts
HAVING [distance] < 15 // Invalid column Name
ORDER BY distance

Having/Where clause not working for Haversine Formula using Microsoft SQL

Here is my table named "test" -
Check the snapshot here test
And then my row items: here - rowitems
I'm using the haversine formula, he is my query
SELECT *, ( 3960 * acos( cos( radians( 33.650800 ) ) *
cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians( -117.891729 ) ) +
sin( radians( 33.650800 ) ) * sin( radians( Latitude ) ) ) ) AS Distance
FROM test
For some reason the HAVING or WHERE clause is not working with Distance.
It works with Latitude or Longitude.
But when I try to do WHERE Distance < 10 or HAVING Distance < 10. It says Distance is an invalid column name.
I need to be able to do this, and make a query using Distance. Any help would be appreciated.
You cannot use calculated fields on a where or having clause. Create a view or use a subquery
Try this:
select * FROM (SELECT *, ( 3960 * acos( cos( radians( 33.650800 ) ) *
cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians( -117.891729 ) ) +
sin( radians( 33.650800 ) ) * sin( radians( Latitude ) ) ) ) AS Distance
FROM test) as T WHERE T.Distance < 10
You need to put your query into a subquery, or a view, or a CTE (common table expression).
Here is an example for your task with a CTE:
WITH cte_test (Name, Latitude, Longitude, Distance)
AS
(
SELECT Name, Latitude, Longitude,
3960 * acos(cos(radians(33.650800))
* cos(radians( Latitude ) )
* cos( radians( Longitude ) - radians( -117.891729 ) )
+ sin( radians( 33.650800 ) ) * sin( radians( Latitude ) ) ) )
AS Distance
FROM test
)
SELECT * from cte_test where Distance < 10 ;
A CTE is a kind of "temporary view". It is also a powerful instrument which can also be used for creating recursive queries.

Google map + places near to current location from SQL Server with haversine formulae

I am trying to display a list of general stores that are near to the current map location.
Everything works fine but I just realised no matter where I pan the map to, a lot of stores that are no where near to the current map location gets call from my SQL Server DB.
Example:
I have a store which resides in Germany in the DB:
Venue Venue_Lat Venue_Lng
Bookstore 51.165712 10.451547
I am using the Haversine formulae in my Stored Procedure [GetNearbyLocations] to retrieve the stores that are suppose to be within 50 miles of the current location, say I pass in a current location as USA with a radius of 50 miles:
Radius: 50
USA Lat: 38.5554745
USA Lng: -95.6649999
The store in Germany gets called as well even though its not within the 50 miles radius. I understand and accept that the formulae is not 100% accurate but is this usual? Can anyone please kindly advice on this. Thanks.
A portion of the SProc:
DECLARE #intMilesModifier int
SET #intMilesModifier = 3959
SELECT .... some fields,
[Venue_Lat],
[Venue_Long],
(#intMilesModifier*acos(cos(radians(#dmlLat))*cos(radians(Venue_Lat))*cos(radians(Venue_Long)-radians(#dmlLng))+sin(radians(#dmlLat))*sin(radians(Venue_Lat)))) AS distance
FROM [Stores] a
WHERE (#intMilesModifier*acos(cos(radians(#dmlLat))*cos(radians(Venue_Lat))*cos(radians(Venue_Long)-radians(#dmlLng))+sin(radians(#dmlLat))*sin(radians(Venue_Lat)))) < #Radius
This PHP/MySQL PDO SQL statement uses the formula (6371 for kilometers,3959 for miles)
$stmt = $dbh->prepare("SELECT name, lat, lng, ( 3959 * acos( cos( radians(?) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(?) ) + sin( radians(?) ) * sin( radians( lat ) ) ) ) AS distance FROM gbstn HAVING distance < ? ORDER BY distance LIMIT 0 , 20");
// Assign parameters
$stmt->bindParam(1,$center_lat);//coordinate of center
$stmt->bindParam(2,$center_lng);//coordinate of center
$stmt->bindParam(3,$center_lat);
$stmt->bindParam(4,$radius);
You will need to modify this to suit
EDIT
You have extra calculations in your formula. I have edited my statement to show what you should have. I removed [] brackets and a from [Stores] a, don't need alias, but cant run query as I dont have SQL Server.
"SELECT Venue_Lat,Venue_Long,(#intMilesModifier * acos( cos( radians(#dmlLat) ) * cos( radians( Venue_Lat ) ) * cos( radians( Venue_Long ) - radians(#dmlLng) ) + sin( radians(#dmlLat) ) * sin( radians( Venue_Lat ) ) ) ) AS distance FROM Stores HAVING distance < #Radius ORDER BY distance LIMIT 0 , 20"

How to implement distance queries in CakePHP?

I have a query (from Google Maps) like so:
SELECT ( 3959 * acos( cos( radians(MY_LAT) ) * cos( radians( LATITUDE ) ) *
cos( radians( LONGITUDE ) - radians(MY_LONG) ) + sin( radians(MY_LAT) ) * sin( radians(
LATITUDE ) ) ) ) AS distance
FROM zips
ORDER BY distance
I would rather this be a custom find in Cakephp, so how do I do that?
MY_LAT and MY_LONG are params passed in.
LATITUDE and LONGITUDE are columns in zips table.
You can use a behavior like the geocoder behavior ( https://github.com/dereuromark/tools/blob/2.0/Model/Behavior/GeocoderBehavior.php#L208 ) and attach it to your model with
$this->Model->Behaviors->attach('Tools.Geocoder');
and call
$this->Model->setDistanceAsVirtualField($lat, $lng, $fieldName);
this way a virtual field is attached and will retrieve and contain your distance value just like any other normal field. you can also sort/filter by that field then.