sql >> Database >  >> RDS >> Mysql

Converteer een complexe SQL-query naar SQLAlchemy

Je HAVING correct wordt behandeld, maar u geeft het de verkeerde uitdrukking door. Het lijkt erop dat je Python 2 gebruikt, aangezien de relationele vergelijking tussen een string en een geheel getal

'distance' < 25

genereert geen uitzondering, maar evalueert tot False in plaats van. Met andere woorden, uw zoekopdracht is gelijk aan

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

wat verklaart waarom je nul resultaten krijgt:alle rijen worden expliciet uitgefilterd door de HAVING-clausule, zoals te zien is in de gedrukte versie:

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

Een oplossing is om een ​​geschikte constructie te gebruiken, zoals column() , om de uitdrukking te produceren:

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

U moet de complexe uitdrukking van het geselecteerde lijstitem niet verpakken in een select() , wat een SELECT-instructie vertegenwoordigt. Ofwel label de text() zoals het is:

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

of bouw de uitdrukking met behulp van het model:

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

U kunt de leesbaarheid van uw queryconstructie verbeteren door een functie te maken voor de grote-cirkelafstand , en met een beetje werk zou je een hybride methode op 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()

Merk op dat de manier waarop u HAVING gebruikt om niet-groepsrijen te elimineren, niet overdraagbaar is. Bijvoorbeeld in Postgresql de aanwezigheid van clausule HAVING verandert een query in een gegroepeerde query, zelfs zonder een GROUP BY-clausule. U kunt in plaats daarvan een subquery gebruiken:

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. Standaardwaarde voor kolom DATE-type instellen op huidige datum zonder tijdsdeel?

  2. Fix "FOUT:elke INTERSECT-query moet hetzelfde aantal kolommen hebben" in PostgreSQL

  3. Hoe gebruik je een variabelenaam in een SQL-instructie?

  4. SQL Server 2008 Rij Tijdstempels invoegen en bijwerken