sql >> Databasteknik >  >> RDS >> Mysql

Konvertera en komplex SQL-fråga till SQLAlchemy

Din HAVING hanteras korrekt, men du skickar det till fel uttryck. Det verkar som att du använder Python 2, eftersom relationsjämförelsen mellan en sträng och ett heltal

'distance' < 25

ger inget undantag, men utvärderas till False istället. Med andra ord är din fråga lika med

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

vilket förklarar varför du får noll resultat:alla rader filtreras explicit bort av HAVING-satsen, som visas i den tryckta versionen:

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

En lösning är att använda en lämplig konstruktion, såsom column() , för att producera uttrycket:

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

Du bör inte slå in det komplexa uttrycket för urvalslistobjekt i en select() , som representerar en SELECT-sats. Märk antingen text() som den är:

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')

eller bygg uttrycket med modellen:

(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')

Du kan förbättra läsbarheten för din frågekonstruktion genom att skapa en funktion för storcirkelavståndet a> , och med lite arbete kan du implementera en hybridmetodLocation :

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()

Observera att sättet du använder HAVING för att eliminera icke-grupprader inte är portabelt. Till exempel i Postgresql förekomsten av HAVING-klausul a> omvandlar en fråga till en grupperad fråga, även utan en GROUP BY-sats. Du kan använda en underfråga istället:

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()        



  1. Hämta data från lagrad procedur med Entity Framework

  2. MySQL trigger/procedur exekveringsfördröjning

  3. MySQL-uppdatering ändra flera kolumner är icke-atomär?

  4. 911/112:En nödsamtalstjänstdatamodell