SQLAlchemy Harvesine ошибка вычисления большого круга - PullRequest
0 голосов
/ 09 января 2019

У меня есть следующая таблица:

id     zip    city     state    longitude    latitude  timezone    dst
3817   99950    Ketchikan    AK    -131.46633      55.875767    -9           1
....

В моей модели SQLALchemy у меня есть следующий @classmethod Я хочу вернуть все почтовые индексы в пределах X миль от заданного лат Я создал два запроса, но оба запроса ниже не удалось выполнить query1, query2

@classmethod
def getZipsWithinXMiles(cls, lat1: str, lon1: str, dst: str):
    """ Get zip codes within 'dst' miles of lat1,lon1"""

    print(lat1, lon1, dst)
    breakpoint()
    query1 = "SELECT * ," \
        "( 3958.75 * acos(sin(lat1/57.2958) * sin(latitude/57.2958) + " \
        "cos(lat1/57.2958) * cos(latitude/57.2958) * " \
        "cos(longitude/57.2958 - lon1/57.2958))" \
        ") as distanceInMiles " \
        "FROM ZipCode " \
        "HAVING distanceInMiles < dst " \
        "ORDER BY distanceInMiles" \
        % {'lat1': float(lat1), 'lon1': float(lon1), 'dst': int(dst)}

    query2 = "SELECT * FROM " \
        "(SELECT id, city, state, zip, " \
        "( 3958.75 * acos(sin(lat1/57.2958) * sin(latitude/57.2958) + " \
        "cos(lat1/57.2958) * cos(latitude/57.2958) * " \
        "cos(longitude/57.2958 - lon1/57.2958))" \
        ") as distanceInMiles " \
        "FROM ZipCode " \
        " ) inner_query " \
        "HAVING distanceInMiles < dst " \
        "ORDER BY distanceInMiles" \
        % {'lat1': float(lat1), 'lon1': float(lon1), 'dst': int(dst)}

    print(query1)
    print(query2)
    breakpoint()
    zipCodes = cls.query.from_statement(query2).all()
    return zipCodes

Это ошибка, которую я получаю:

cursor.execute(statement, parameters)
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) relation "zipcode" does not exist
LINE 1: ...57.2958 - lon1/57.2958))) as distanceInMiles FROM ZipCode  )...

Просто для получения дополнительной информации, это модель, которую я использовал для создания таблицы

class ZipCode(db.Model):

    __tablename__ = "zipCode"

    id = db.Column(db.Integer, primary_key=True)
    zip = db.Column(db.String(5), nullable=False)  # 23567
    city = db.Column(db.String(50), nullable=False)  # New York
    state = db.Column(db.String(2), nullable=False)  # Ex: NY
    longitude = db.Column(db.String(15), nullable=False)
    latitude = db.Column(db.String(15), nullable=False)
    timezone = db.Column(db.String(3), nullable=False)  # Ex: -5,-10
    dst = db.Column(db.String(2))  # Ex: 0,1

Что не так с этим кодом? Я пробовал оба запроса, и оба не удалось

1 Ответ

0 голосов
/ 09 января 2019

С учетом

__tablename__ = "zipCode"

вам придется использовать указанный в кавычках идентификатор для ссылки на таблицу:

query1 = "..." \
    ...
    'FROM "zipCode" ' \
    ...

из-за смешанного регистра :

Кавычка идентификатора также делает его чувствительным к регистру, тогда как имена без кавычек всегда свертываются в нижний регистр. Например, идентификаторы FOO, foo и "foo" в PostgreSQL считаются одинаковыми, но "Foo" и "FOO" отличаются друг от друга.

SQLAlchemy будет использовать заключенные в кавычки идентификаторы, если данное имя таблицы содержит символы верхнего регистра, является зарезервированным словом или содержит специальные символы.


В остальном, использование предложения HAVING для фильтрации по элементам списка выбора является «расширением» MySQL, которое Postgresql не поддерживает. query2 находится на правильном пути в использовании подзапроса, но вместо него следует использовать предложение WHERE.

Кажется, вы определили столбцы latitude и longitude как строки, поэтому вы должны использовать CAST для выполнения арифметики с ними. Попробуйте вместо этого использовать правильные типы.

Вы также использовали% -форматирование, но без каких-либо спецификаторов преобразования в строке - это хорошо, так как вам все равно не следует выполнять форматирование строки. Вместо этого используйте связанные параметры / заполнители и передавайте параметры в драйвер или библиотеку SQL.

В целом ваш метод класса может выглядеть так:

def getZipsWithinXMiles(cls, lat1: str, lon1: str, dst: str):
    """ Get zip codes within 'dst' miles of lat1,lon1"""

    query = db.text("""
        SELECT *
        FROM (
            SELECT "zipCode".*
                 , (3958.75 *
                    acos(sin(:lat1 / 57.2958) * sin(cast(latitude as double precision) / 57.2958) +
                         cos(:lat1 / 57.2958) * cos(cast(latitude as double precision) / 57.2958) *
                         cos(cast(longitude as double precision) / 57.2958 - :lon1 / 57.2958)))
                   as distanceInMiles
            FROM "zipCode") zc
        WHERE zc.distanceInMiles < :dst
        ORDER BY zc.distanceInMiles
        """)

    zipCodes = cls.query.\
        from_statement(query).\
        params(lat1=float(lat1),
               lon1=float(lon1),
               dst=int(dst)).\
        all()

    return zipCodes

хотя я не несу ответственности за правильность расчета расстояния.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...