Правильное определение функции для необязательных параметров запроса SELECT в Python с использованием psycopg2 с POSTGRESQL - PullRequest
0 голосов
/ 01 ноября 2019

Я хочу ВЫБРАТЬ из моей базы данных PostgreSQL, используя дополнительные параметры фильтра в функции, но я пытаюсь получить правильный синтаксис.

def search(title: str = '', author: str = "", year: int, isbn: int):
    conn = psycopg2.connect(
        " dbname='mydb' user='postgres' password='pwd' host='localhost' port='5432' ")
    curs = conn.cursor()
    curs.execute("SELECT * FROM book WHERE title=? OR author=? OR year=? OR isbn=?",
                 (title, author, year, isbn))
    rows = curs.fetchall()
    conn.close()
    return rows

Затем я хочу выполнить поиск, передав только один параметр (имя автора) к функции, и оператор SELECT сможет игнорировать / пропускать поля, которые не были переданы. Когда я выполняю

print(search(author="John"))

Выходные данные

Traceback (most recent call last):
  File "backend.py", line 48, in <module>
    print(search(author="John"))
  File "backend.py", line 39, in search
    (title, author, year, isbn))
psycopg2.errors.UndefinedFunction: operator does not exist: text =?
LINE 1: SELECT * FROM book WHERE title=? OR author=? OR year=? OR is...
                                      ^
HINT:  No operator matches the given name and argument type. You might need to add an explicit type cast.

Я понимаю, что это связано с несоответствием между типом данных оператора SELECT и типом столбца базы данных, и что ошибка отражаетчто нет оператора = для сравнения этих двух типов данных. Но как мне определить параметры моей функции и оператор SELECT, чтобы заставить мой запрос работать?

Ответы [ 2 ]

2 голосов
/ 01 ноября 2019

psycopg2 использует %s в качестве заполнителя для подстановки строк, а не ?. Попробуйте изменить? % s, и это должно сработать.

Редактировать: @Parfait поднял хорошую мысль о вашей логике где. Меняется? % s исправит вашу синтаксическую ошибку, но все равно не исправит логику. Один из распространенных способов справиться с этим - установить по умолчанию все ваши параметры на None и изменить запрос следующим образом:

curs.execute("""
    SELECT * FROM BOOK
    WHERE (title = %(title)s OR %(title)s IS NULL)
      AND (author = %(author)s OR %(author)s IS NULL)
      AND (year = %(year)s OR %(year)s IS NULL)
      AND (isbn = %(isbn)s OR %(isbn)s IS NULL)""",
    {"title": title, "author": author, "year": year, "isbn": isbn})

Это не самый эффективный метод, но, вероятно, самый простой.

0 голосов
/ 01 ноября 2019

В качестве альтернативы рассмотрите передачу по умолчанию None, что переводится как NULL, и используйте SQL COALESCE для выбора первого ненулевого значения. Если параметры None, тогда столбцы в запросе будут равны друг другу, что по существу не применяет фильтр.

def search(title = None, author = None, year = None, isbn = None):
    conn = psycopg2.connect(
        " dbname='mydb' user='postgres' password='pwd' host='localhost' port='5432' ")

    sql = """SELECT * FROM book 
             WHERE COALESCE(title, 'N/A') = COALESCE(%s, title, 'N/A')
               AND COALESCE(author, 'N/A') = COALESCE(%s, author, 'N/A')
               AND COALESCE(year, -1) = COALESCE(%s, year, -1) 
               AND COALESCE(isbn, -1) = COALESCE(%s, isbn, -1)
          """

    curs = conn.cursor()
    curs.execute(sql, (title, author, year, isbn))
    rows = curs.fetchall()
    conn.close()

    return rows
...