Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Chuyển đổi một truy vấn SQL phức tạp thành SQLAlchemy

HAVING của bạn được xử lý chính xác, nhưng bạn đang chuyển nó biểu thức sai. Có vẻ như bạn đang sử dụng Python 2, vì so sánh quan hệ giữa một chuỗi và một số nguyên

'distance' < 25

không đưa ra một ngoại lệ, nhưng đánh giá thành False thay thế. Nói cách khác, truy vấn của bạn bằng

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

điều này giải thích tại sao bạn không nhận được kết quả nào:tất cả các hàng được lọc ra rõ ràng bằng mệnh đề HAVING, như được thấy trong phiên bản in:

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

Giải pháp là sử dụng một cấu trúc phù hợp, chẳng hạn như column() , để tạo ra biểu thức:

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

Bạn không nên bọc biểu thức mục danh sách chọn phức tạp trong select() , đại diện cho một câu lệnh SELECT. Gắn nhãn cho text() như là:

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

hoặc xây dựng biểu thức bằng mô hình:

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

Bạn có thể cải thiện khả năng đọc cấu trúc truy vấn của mình bằng cách tạo một hàm cho khoảng cách vòng tròn lớn và với một chút công việc, bạn có thể triển khai phương pháp kết hợp trên 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()

Lưu ý rằng cách bạn sử dụng HAVING để loại bỏ các hàng không thuộc nhóm không phải là cách di động. Ví dụ:trong Postgresql, sự hiện diện của mệnh đề HAVING chuyển một truy vấn thành một truy vấn được nhóm, ngay cả khi không có mệnh đề GROUP BY. Thay vào đó, bạn có thể sử dụng một truy vấn con:

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. Mysql - 30 ngày qua

  2. Có ANSI SQL thay thế cho từ khóa MYSQL LIMIT không?

  3. Kiểm tra xem tất cả các giá trị trong một mảng có tồn tại trong một cột cơ sở dữ liệu hay không

  4. MySQL - Trừ giá trị từ hàng trước, nhóm theo

  5. Đòi lại dung lượng đĩa sau khi thả cơ sở dữ liệu trong mysql