There's no quick and easy way but for the benefit of someone else with the same problem the solution is to convert the geography type to a geometry type, take the bounding box (envelope) of the geometry and pull out the 1st (min) and 3rd (max) points.
CGMinLong AS (CONVERT(float, geography::STGeomFromWKB( geometry::STGeomFromWKB(contactpolygon.STAsBinary(), contactpolygon.STSrid).STEnvelope().STAsBinary(), contactPolygon.STSrid).MakeValid().STPointN(1).Long)) PERSISTED,
CGMinLat AS (CONVERT(float,geography::STGeomFromWKB( geometry::STGeomFromWKB(contactpolygon.STAsBinary(), contactpolygon.STSrid).STEnvelope().STAsBinary(), contactPolygon.STSrid).MakeValid().STPointN(1).Lat)) PERSISTED,
CGMaxLong AS (CONVERT(float,geography::STGeomFromWKB( geometry::STGeomFromWKB(contactpolygon.STAsBinary(), contactpolygon.STSrid).STEnvelope().STAsBinary(), contactPolygon.STSrid).MakeValid().STPointN(3).Long)) PERSISTED,
CGMaxLat AS (CONVERT(float,geography::STGeomFromWKB( geometry::STGeomFromWKB(contactpolygon.STAsBinary(), contactpolygon.STSrid).STEnvelope().STAsBinary(), contactPolygon.STSrid).MakeValid().STPointN(3).Lat)) PERSISTED
Even with the conversions between types and WKB it still performs pretty well and if you use these min and max values instead of the spatial index (for retrieving shapes within a rectangle for display on the screen) you might find, like i have, that it performs more consistently and normally better than a spatial index search.
The search ends up looking like:
WHERE not(CGMaxLong < @minLon OR CGMinLong > @maxLon OR CGMaxLat < @minLat OR CGMinLat > @maxLat
where the @ parameters are your search area.
The reason databases implement R-tree indexes for spatial is because they are faster than geohashes or searches on separate x and y indexes. The problem with geohashes, is that you have to search 9 quadrants, not just 1, to do proximity type searches -- see geohash limitations. They are useful in databases that lack R-trees, to allow the expression of an object with a 2-D range, in one dimension, which can then be indexed with a B-tree. Having separate (or compound) indexes on x and y will also be slower, as you need to scan more of the index to zero in on your area of interest, while with R-trees, you index search is on the bounding box.
Usage will vary, but it is not overkill to use spatial just because you only have points. You lose nothing by using a geometry type and potentially gain a lot (not just in terms of speed), but in future proofing. What if you want to add buffering or polygon intersection at a later date? Ultimately, the only way to know is to test your use case, but my 2c is use approach 1.
Best Answer
You should be able to do this with the EnvelopeAggregate function. I only tested using geometry data type, but let me know if this works for geography too...
Here is the SQL syntax for selecting a group of points and intersecting them against the bounding box of another set of points: