psycopg2 TypeError: 在字符串格式化过程中未转换所有参数

72

我试图执行一个简单的查询,但无论如何传递参数都会出现这个错误。

这是查询语句(我正在使用Trac db对象连接到一个数据库):

cursor.execute("""SELECT name FROM "%s".customer WHERE firm_id='%s'""" % (schema, each['id']))

模式和each ['id']都是简单字符串

print("""SELECT name FROM "%s".customer WHERE firm_id='%s'""" % (schema, each['id']))

结果: SELECT name FROM "Planing".customer WHERE firm_id='135'

这里有一个错误是在firm_id=后面漏了一个引号,但这样参数就被当作整数处理,而::text也会导致同样的错误。


你能添加 print("""SELECT name FROM "%s".customer WHERE firm_id='%s'""" % (schema, each['id'])) 的输出吗? - Nigel Tufnel
我已经更新了我的问题,并附上了输出结果。 - konart
7个回答

184

在我的情况下,我没有意识到必须向cursor.execute传递一个元组。我之前写的是:

cursor.execute(query, (id))

但是我需要传递一个元组

cursor.execute(query, (id,))

116
这是世界上最愚蠢的事情。在我使用过的所有编程语言模块中,这必须是我见过的最不专业的陷阱之一。他们无法使其正常工作,而不必随机插入逗号(转换为元组)? - Nate L
2
顺便说一下,即使我的输入是元组,我仍然不得不将其转换为元组(因此是元组的元组)。同意@NateL - 这真糟糕。 - Adam Hughes
4
Python最好!讨厌这条蛇。 - Alex Barysevich
1
哦,我多么想念这个!很好,抓住了。 - confiq
1
@NateL 括号在不同的上下文中有不同的意义。如果括号包含一个逗号分隔的列表,则它们变成元组;否则,如果它们仅包含一件事情,则成为单个表达式。因此,如果您想要一个项目的元组,则逗号告诉Python将其视为元组而不是表达式。 - Steven
把我加入那些为此苦苦思索、尝试各种方法,最终偶然发现解决方案的人之列。谢谢你。同意其他人的挫败感 :( - JeopardyTempest

35

我遇到了相同的错误,但我一直无法弄清楚如何修复它,最终发现是我的错误,因为我没有足够的参数匹配元组中的元素数量:

con.execute("INSERT INTO table VALUES (%s,%s,%s,%s,%s)",(1,2,3,4,5,6))

请注意,我要插入到表中的值有5个元素,但是元组中有6个。


2
谢谢!这个观察帮助我检查并发现我做了类似愚蠢的事情! :) - bigsee
1
谢谢!我犯了同样的错误,只有在阅读你的答案后才检查。 - Gabriela Catalina
1
谢谢。很难以那种方式解释错误信息! - Je Je
如果使用 to_records 函数,请确保使用参数 df.to_records(index=False) - M.Viking

12

建议不要在数据库查询中使用字符串插值来传递变量,但是可以使用字符串插值来设置表名,只要它不是外部输入或者您限制了允许的值。尝试:

cursor.execute("""
    SELECT name FROM %s.customer WHERE firm_id=%%s
    """ % schema, (each['id'],))

DB API使用规则提供了针对数据库编程的指导。


警告永远不要使用Python字符串拼接(+)或字符串参数插值(%)将变量传递给SQL查询字符串。即使在枪口下也不行。他们甚至明确表示,您不应该对任何具有占位符的查询使用“%”。 - Cheruvim

8
使用 AsIs
from psycopg2.extensions import AsIs

cursor.execute("""
    select name 
    from %s.customer 
    where firm_id = %s
    """, 
    (AsIs(schema), each['id'])
)

7
你可以尝试这个:
cursor.execute("INSERT INTO table_name (key) VALUES(%s)",(value1,))

如果你在value1后面缺少了(,),你将会收到一个错误提示。


1
这已经在这个答案中提到了... - Sven Eberth

5
在SQL命令中传递变量的正确方法是使用 execute()方法的第二个参数。我认为您应该从第二个参数中删除单引号,可以在此处了解更多信息:http://initd.org/psycopg/docs/usage.html#the-problem-with-the-query-parameters
请注意,您不能将表名作为参数传递给 execute ,这被认为是不好的做法,但有一些解决方法:
以参数形式传递表名
psycopg2 cursor.execute() with SQL query parameter causes syntax error 要传递表格名称,请尝试以下内容:
cursor.execute("""SELECT name FROM "%s".customer WHERE firm_id=%s""" % (schema, '%s'), (each['id'],))

是的,这就是我在搜索过程中发现的问题,但这种方式会引发以下错误:LINE 1: SELECT name FROM "'Planing'".customer WHERE firm_id='135' -- 有没有一种方法可以删除 'Planing' 附近的单引号? - konart
它跟我的 print("""SELECT name FROM "%s".customer WHERE firm_id='%s'""" % (schema, each['id'])) 有什么不同呢?虽然这个方法可以运行,但是不是你之前说的不正确的方式吗? - konart
谢谢,我以前已经阅读了该链接。对我来说主要问题是模式名称可能会更改,因此硬编码也不是最佳选项,而正确的方法可以确保我传递为表值的字符串将被转义。我猜现在我不得不坚持硬编码。 - konart

1
每次我遇到这种错误,都是因为我传递了错误数量的值。请尝试检查一下。

这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - Simas Joneliunas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接