Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Mengonversi kueri SQL kompleks ke SQLAlchemy

HAVING . Anda ditangani dengan benar, tetapi Anda memberikan ekspresi yang salah. Sepertinya Anda menggunakan Python 2, karena perbandingan relasional antara string dan integer

'distance' < 25

tidak memunculkan pengecualian, tetapi mengevaluasi ke False sebagai gantinya. Dengan kata lain, kueri Anda sama dengan

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

yang menjelaskan mengapa Anda mendapatkan hasil nol:semua baris secara eksplisit difilter oleh klausa HAVING, seperti yang terlihat dalam versi cetak:

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

Solusinya adalah dengan menggunakan konstruksi yang sesuai, seperti column() , untuk menghasilkan ekspresi:

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

Anda tidak boleh membungkus ekspresi item daftar pilihan yang kompleks dalam select() , yang mewakili pernyataan SELECT. Beri label pada text() seperti:

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

atau buat ekspresi menggunakan 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')

Anda dapat meningkatkan keterbacaan konstruksi kueri Anda dengan membuat fungsi untuk jarak lingkaran besar , dan dengan sedikit usaha Anda dapat menerapkan metode hibrida di 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()

Perhatikan bahwa cara Anda menggunakan HAVING untuk menghilangkan baris non-grup tidak portabel. Misalnya di Postgresql adanya klausa HAVING mengubah kueri menjadi kueri yang dikelompokkan, bahkan tanpa klausa GROUP BY. Anda bisa menggunakan subquery sebagai gantinya:

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Perlu Mengonversi Kolom menjadi Baris di MYSQL

  2. Ubah fungsi ke PDO

  3. pandas — Menggabungkan kolom string tidak berfungsi (bug?)

  4. MySQL melewatkan baris pertama

  5. Spring-Boot, Tidak dapat menyimpan string unicode di MySql menggunakan spring-data JPA