将复杂的sql查询转换为sqlalchemy

vtwuwzda  于 2021-06-24  发布在  Mysql
关注(0)|答案(1)|浏览(511)

我没有主意了。我在谷歌上搜索了一天多,仍然找不到任何有用的答案来回答我的问题。
到目前为止,我一直尝试使用原始sql,但运气不佳。

  1. 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() 从句正确。
打印时,我的查询如下所示:

  1. 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
  2. FROM location
  3. HAVING false = 1

如何将此sql查询转换为sqlalchemy

  1. 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;

我的table是这样的:

  1. +--------------+----------------+------+-----+---------+-------+
  2. | Field | Type | Null | Key | Default | Extra |
  3. +--------------+----------------+------+-----+---------+-------+
  4. | id | varchar(50) | NO | PRI | NULL | |
  5. | created_date | datetime | YES | | NULL | |
  6. | zip | varchar(5) | NO | UNI | NULL | |
  7. | user_id | varchar(50) | NO | | NULL | |
  8. | lat | decimal(15,13) | NO | | NULL | |
  9. | lng | decimal(15,13) | NO | | NULL | |
  10. | city | text | NO | | NULL | |
  11. +--------------+----------------+------+-----+---------+-------+

感谢您的帮助。

wnvonmuf

wnvonmuf1#

你的 HAVING 处理正确,但传递的表达式错误。似乎您使用的是python2,因为字符串和整数之间的关系比较

  1. 'distance' < 25

不引发异常,但计算结果为 False 相反。换句话说,您的查询等于

  1. locations = db.session.query(...).having(False).all()

这就解释了为什么结果为零:所有行都被having子句显式过滤掉,如打印版本所示:

  1. ...
  2. HAVING false = 1 -- remove all rows

解决方法是使用合适的构造,例如 column() ,生成表达式:

  1. locations = db.session.query(...).having(column('distance') < 25).all()

不应该将复杂的select列表项表达式 Package 在 select() ,它表示select语句。要么给 text() 原样:

  1. text('( 6371 * acos( cos( radians("53.6209798282177") ) * '
  2. 'cos( radians( lat ) ) * cos( radians( lng ) - radians("13.96948162900808") ) + '
  3. 'sin( radians("53.6209798282177") ) * sin( radians( lat ) ) ) ) '
  4. 'AS distance')

或使用模型构建表达式:

  1. (6371 *
  2. func.acos(func.cos(func.radians(53.6209798282177)) *
  3. func.cos(func.radians(Location.lat)) *
  4. func.cos(func.radians(Location.lng) - func.radians(13.96948162900808)) +
  5. func.sin(func.radians(53.6209798282177)) *
  6. func.sin(func.radians(Location.lat)))).label('distance')

您可以通过为大圆距离生成一个函数来提高查询构造的可读性,并且只需做一点工作,就可以在上实现一个混合方法 Location :

  1. import math
  2. def gc_distance(lat1, lng1, lat2, lng2, math=math):
  3. ang = math.acos(math.cos(math.radians(lat1)) *
  4. math.cos(math.radians(lat2)) *
  5. math.cos(math.radians(lng2) -
  6. math.radians(lng1)) +
  7. math.sin(math.radians(lat1)) *
  8. math.sin(math.radians(lat2)))
  9. return 6371 * ang
  10. class Location(db.Model):
  11. ...
  12. @hybrid_method
  13. def distance(self, lat, lng):
  14. return gc_distance(lat, lng, self.lat, self.lng)
  15. @distance.expression
  16. def distance(cls, lat, lng):
  17. return gc_distance(lat, lng, cls.lat, cls.lng, math=func)
  18. locations = db.session.query(
  19. Location,
  20. Location.distance(53.6209798282177,
  21. 13.96948162900808).label('distance')).\
  22. having(column('distance') < 25).\
  23. order_by('distance').\
  24. all()

请注意,消除非组行的方法是不可移植的。例如,在postgresql中,having子句的存在将查询转换为分组查询,即使没有groupby子句。您可以改用子查询:

  1. stmt = db.session.query(
  2. Location,
  3. Location.distance(53.6209798282177,
  4. 13.96948162900808).label('distance')).\
  5. subquery()
  6. location_alias = db.aliased(Location, stmt)
  7. locations = db.session.query(location_alias).\
  8. filter(stmt.c.distance < 25).\
  9. order_by(stmt.c.distance).\
  10. all()
展开查看全部

相关问题