51

psycopg2 是否具有转义Postgres的LIKE操作数的值的功能?

例如,我可能想匹配以字符串“20% of all”开头的字符串,所以我想写这样的东西:

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

我可以在这里插入一个现有的escape_sql_like函数吗?

(与如何明确引用字符串值(Python DB API/Psycopg2)类似的问题,但我在那里找不到答案。)

4

12 回答 12

35

Yeah, this is a real mess. Both MySQL and PostgreSQL use backslash-escapes for this by default. This is a terrible pain if you're also escaping the string again with backslashes instead of using parameterisation, and it's also incorrect according to ANSI SQL:1992, which says there are by default no extra escape characters on top of normal string escaping, and hence no way to include a literal % or _.

I would presume the simple backslash-replace method also goes wrong if you turn off the backslash-escapes (which are themselves non-compliant with ANSI SQL), using NO_BACKSLASH_ESCAPE sql_mode in MySQL or standard_conforming_strings conf in PostgreSQL (which the PostgreSQL devs have been threatening to do for a couple of versions now).

The only real solution is to use the little-known LIKE...ESCAPE syntax to specify an explicit escape character for the LIKE-pattern. This gets used instead of the backslash-escape in MySQL and PostgreSQL, making them conform to what everyone else does and giving a guaranteed way to include the out-of-band characters. For example with the = sign as an 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+'%'))

This works on PostgreSQL, MySQL, and ANSI SQL-compliant databases (modulo the paramstyle of course which changes on different db modules).

There may still be a problem with MS SQL Server/Sybase, which apparently also allows [a-z]-style character groups in LIKE expressions. In this case you would want to also escape the literal [ character with .replace('[', '=['). However according to ANSI SQL escaping a character that doesn't need escaping is invalid! (Argh!) So though it will probably still work across real DBMSs, you'd still not be ANSI-compliant. sigh...

于 2010-01-21T02:04:04.760 回答
29

我能够%通过%%LIKE操作数中使用来逃脱。

sql_query = "select * from mytable where website like '%%.com'"
cursor.fetchall(sql_query)
于 2019-08-27T22:48:05.070 回答
5

你也可以从不同的角度来看待这个问题。你想要什么?您希望查询对于任何字符串参数都执行 LIKE,方法是在参数上附加一个 '%'。一个很好的表达方式,而不诉诸函数和 psycopg2 扩展可能是:

sql = "... WHERE ... LIKE %(myvalue)s||'%'"
cursor.execute(sql, { 'myvalue': '20% of all'})
于 2013-02-13T10:01:11.147 回答
4

如果您使用的是准备好的语句,那么输入将被包装''以防止 sql 注入。这很棒,但也可以防止输入 + sql 连接。

解决此问题的最佳和最安全的方法是将%(s) 作为输入的一部分传入。

cursor.execute('SELECT * FROM goats WHERE name LIKE %(name)s', { 'name': '%{}%'.format(name)})
于 2019-11-20T08:55:13.953 回答
2

您可以使用 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 方面混淆水域。

于 2014-02-06T18:02:26.713 回答
2

我想知道是否真的需要以上所有内容。我正在使用 psycopg2 并且可以简单地使用:

data_dict['like'] = psycopg2.Binary('%'+ match_string +'%')
cursor.execute("SELECT * FROM some_table WHERE description ILIKE %(like)s;", data_dict)
于 2010-06-02T06:03:40.217 回答
2

我找到了一个更好的 hack。只需将“%”附加到您的搜索 query_text。

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+'%'))
于 2018-08-27T18:14:02.083 回答
1

到目前为止找不到内置函数,我写的很简单:

def escape_sql_like(s):
    return s.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
于 2010-01-21T01:22:11.213 回答
0

您可以创建一个Like类的子类化str为其注册一个适配器,以使其以正确的语法进行转换(例如,使用escape_sql_like()您编写的)。

于 2010-02-16T10:45:18.327 回答
0

我对上面的代码进行了一些修改以执行以下操作:

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)
于 2011-08-04T04:48:28.533 回答
0

它只需要在它之前和之后连接双 % 。使用“ilike”而不是“like”使其不区分大小写。

query = """
    select 
        * 
    from 
        table 
    where 
        text_field ilike '%%' || %(search_text)s || '%%'
"""
于 2021-11-19T21:51:39.013 回答
-1

我认为使用 f-strings 会更简单、更易读。

query = f'''SELECT * FROM table where column like '%%{my_value}%%' '''
cursor.execute(query)
于 2020-08-07T21:08:16.637 回答