[英]Convert a complex SQL query to SQLAlchemy
我沒有想法。 我現在用Google搜索了一天以上,我仍然找不到任何有用的答案。
直到現在我做了什么,我試圖使用原始SQL,但沒有運氣。
locations = db.session.query(Location, select([text('( 6371 * acos( cos( radians("53.6209798282177") ) * cos( radians( lat ) ) * cos( radians( lng ) - radians("13.96948162900808") ) + sin( radians("53.6209798282177") ) * sin( radians( lat ) ) ) )')]).label('distance')).having('distance' < 25).all()
當使用這個原始SQL查詢時,我返回零結果,但是當在mysql
運行相同的查詢時,它返回正確的結果。
我進一步想到,當將查詢打印到終端時,它不能正確處理HAVING()
子句。
打印時我的查詢如下所示:
SELECT location.id AS location_id, location.created_date AS location_created_date, location.zip AS location_zip, location.user_id AS location_user_id, location.lat AS location_lat, location.lng AS location_lng, location.city AS location_city
FROM location
HAVING false = 1
如何將此SQL查詢轉換為SQLAlchemy
SELECT *, ( 6371 * acos( cos( radians(53.6209798282177) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(11.96948162900808) ) + sin( radians(53.6209798282177) ) * sin( radians( lat ) ) ) ) AS distance FROM location HAVING distance < 25 ORDER BY distance;
我的表看起來像這樣:
+--------------+----------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------------+------+-----+---------+-------+
| id | varchar(50) | NO | PRI | NULL | |
| created_date | datetime | YES | | NULL | |
| zip | varchar(5) | NO | UNI | NULL | |
| user_id | varchar(50) | NO | | NULL | |
| lat | decimal(15,13) | NO | | NULL | |
| lng | decimal(15,13) | NO | | NULL | |
| city | text | NO | | NULL | |
+--------------+----------------+------+-----+---------+-------+
任何幫助表示贊賞。
你的HAVING
處理得當,但是你傳遞的是錯誤的表達方式。 看來你正在使用Python 2,因為字符串和整數之間的關系比較
'distance' < 25
不會引發異常,而是評估為False
。 換句話說,您的查詢等於
locations = db.session.query(...).having(False).all()
這解釋了為什么你得到零結果:所有行都被HAVING子句顯式過濾掉,如印刷版本所示:
...
HAVING false = 1 -- remove all rows
解決方案是使用合適的構造(例如column()
來生成表達式:
locations = db.session.query(...).having(column('distance') < 25).all()
您不應將復雜的選擇列表項表達式包裝在select()
,該表示SELECT語句。 將text()
標記為:
text('( 6371 * acos( cos( radians("53.6209798282177") ) * '
'cos( radians( lat ) ) * cos( radians( lng ) - radians("13.96948162900808") ) + '
'sin( radians("53.6209798282177") ) * sin( radians( lat ) ) ) ) '
'AS distance')
或使用模型構建表達式:
(6371 *
func.acos(func.cos(func.radians(53.6209798282177)) *
func.cos(func.radians(Location.lat)) *
func.cos(func.radians(Location.lng) - func.radians(13.96948162900808)) +
func.sin(func.radians(53.6209798282177)) *
func.sin(func.radians(Location.lat)))).label('distance')
您可以通過為大圓距離創建函數來提高查詢構造的可讀性,並且通過一些工作,您可以在Location
上實現混合方法 :
import math
def gc_distance(lat1, lng1, lat2, lng2, math=math):
ang = math.acos(math.cos(math.radians(lat1)) *
math.cos(math.radians(lat2)) *
math.cos(math.radians(lng2) -
math.radians(lng1)) +
math.sin(math.radians(lat1)) *
math.sin(math.radians(lat2)))
return 6371 * ang
class Location(db.Model):
...
@hybrid_method
def distance(self, lat, lng):
return gc_distance(lat, lng, self.lat, self.lng)
@distance.expression
def distance(cls, lat, lng):
return gc_distance(lat, lng, cls.lat, cls.lng, math=func)
locations = db.session.query(
Location,
Location.distance(53.6209798282177,
13.96948162900808).label('distance')).\
having(column('distance') < 25).\
order_by('distance').\
all()
請注意,使用HAVING消除非組行的方式不可移植。 例如,在Postgresql中,即使沒有GROUP BY子句, HAVING子句的存在也會將查詢轉換為分組查詢。 您可以使用子查詢:
stmt = db.session.query(
Location,
Location.distance(53.6209798282177,
13.96948162900808).label('distance')).\
subquery()
location_alias = db.aliased(Location, stmt)
locations = db.session.query(location_alias).\
filter(stmt.c.distance < 25).\
order_by(stmt.c.distance).\
all()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.