Выход из SQL "LIKE" для Postgres с помощью psycopg2 - PullRequest
30 голосов
/ 21 января 2010

Есть ли у psycopg2 функция для экранирования значения операнда LIKE для Postgres?

Например, я могу захотеть сопоставить строки, которые начинаются со строки «20% от всех», поэтому я хочу написать что-то вроде этого:

sql = '... WHERE ... LIKE %(myvalue)s'
cursor.fetchall(sql, { 'myvalue': escape_sql_like('20% of all') + '%' }

Существует ли существующая escape_sql_like функция, которую я мог бы подключить здесь?

(Подобный вопрос к Как явно указать значение строки (Python DB API / Psycopg2) , но я не смог найти там ответа.)

Ответы [ 8 ]

27 голосов
/ 21 января 2010

Да, это настоящий беспорядок. И MySQL, и PostgreSQL используют для этого обратную косую черту по умолчанию. Это ужасная боль, если вы снова экранируете строку с помощью обратной косой черты вместо использования параметризации, и это также неверно в соответствии с ANSI SQL: 1992, который говорит, что по умолчанию нет никаких дополнительных escape-символов поверх обычного экранирования строки, и следовательно, нет способа включить литерал % или _.

Я бы предположил, что простой метод обратного слеша-замены также идет не так, если вы выключаете экранирование обратного слэша (которые сами не совместимы с ANSI SQL), используя NO_BACKSLASH_ESCAPE sql_mode в MySQL или standard_conforming_strings conf в PostgreSQL ( что разработчики PostgreSQL угрожали сделать для пары версий).

Единственным реальным решением является использование малоизвестного синтаксиса LIKE...ESCAPE для указания явного escape-символа для LIKE -шаблонов. Это используется вместо экранирования обратной косой черты в MySQL и PostgreSQL, заставляя их соответствовать тому, что делают все остальные, и предоставляя гарантированный способ включения внеполосных символов. Например, со знаком = в качестве escape:

# look for term anywhere within title
term= term.replace('=', '==').replace('%', '=%').replace('_', '=_')
sql= "SELECT * FROM things WHERE description LIKE %(like)s ESCAPE '='"
cursor.execute(sql, dict(like= '%'+term+'%'))

Это работает в PostgreSQL, MySQL и ANSI SQL-совместимых базах данных (конечно, по модулю paramstyle, который изменяется в разных модулях БД).

Возможно, все еще существует проблема с MS SQL Server / Sybase, который, по-видимому, также допускает использование групп символов [a-z] в выражениях LIKE. В этом случае вы также хотели бы экранировать буквальный символ [ с .replace('[', '=['). Однако в соответствии с ANSI SQL экранирование символа, который не нуждается в экранировании, недопустимо! (Argh!) Так что, хотя он, вероятно, все еще будет работать в реальных СУБД, вы все равно не будете соответствовать ANSI. Вздох ...

4 голосов
/ 13 февраля 2013

Вы также можете посмотреть на эту проблему под другим углом. Чего ты хочешь? Вы хотите запрос, который для любого строкового аргумента выполняет LIKE, добавляя «%» к аргументу. Хороший способ выразить это, не прибегая к функциям и расширениям psycopg2, можно:

sql = "... WHERE ... LIKE %(myvalue)s||'%'"
cursor.execute(sql, { 'myvalue': '20% of all'})
2 голосов
/ 06 февраля 2014

Вместо экранирования символа процента можно использовать реализацию регулярного выражения в PostgreSQL.

Например, следующий запрос к системным каталогам предоставит список активных запросов, которые не относятся к подсистеме автоочистки:

SELECT procpid, current_query FROM pg_stat_activity
WHERE (CURRENT_TIMESTAMP - query_start) >= '%s minute'::interval
AND current_query !~ '^autovacuum' ORDER BY (CURRENT_TIMESTAMP - query_start) DESC;

Поскольку этот синтаксис запроса не использует ключевое слово «LIKE», вы можете делать то, что вы хотите ... и не пачкаться по отношению к python и psycopg2.

1 голос
/ 02 июня 2010

Интересно, действительно ли все вышесказанное действительно необходимо? Я использую psycopg2 и просто смог использовать:

data_dict['like'] = psycopg2.Binary('%'+ match_string +'%')
cursor.execute("SELECT * FROM some_table WHERE description ILIKE %(like)s;", data_dict)
0 голосов
/ 27 августа 2018

Я нашел лучший взломать. Просто добавьте «%» к вашему поисковому запросу.

con, queryset_list = psycopg2.connect(**self.config), None
cur = con.cursor(cursor_factory=RealDictCursor)
query = "SELECT * "
query += " FROM questions WHERE  body LIKE %s OR title LIKE %s  "
query += " ORDER BY questions.created_at"
cur.execute(query, ('%'+self.q+'%', '%'+self.q+'%'))
0 голосов
/ 04 августа 2011

Я внес некоторые изменения в код выше, чтобы сделать следующее:

def escape_sql_like(SQL):
    return SQL.replace("'%", 'PERCENTLEFT').replace("%'", 'PERCENTRIGHT')

def reescape_sql_like(SQL):
    return SQL.replace('PERCENTLEFT', "'%").replace('PERCENTRIGHT', "%'")

SQL = "SELECT blah LIKE '%OUCH%' FROM blah_tbl ... "
SQL = escape_sql_like(SQL)
tmpData = (LastDate,)
SQL = cur.mogrify(SQL, tmpData)
SQL = reescape_sql_like(SQL)
cur.execute(SQL)
0 голосов
/ 16 февраля 2010

Вы можете создать подклассы Like класса str и зарегистрировать адаптер для него , чтобы преобразовать его в правильный синтаксис (например, используя написанное вами escape_sql_like()).

0 голосов
/ 21 января 2010

Пока не удалось найти встроенную функцию, я написал довольно простую:

def escape_sql_like(s):
    return s.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
...