Python 3.5与3.6版本中sqlite3 UPDATE操作rowcount返回值差异求助
解决Python 3.6+中sqlite3 UPDATE操作rowcount返回-1的问题
我之前碰到过一模一样的问题!这是Python 3.6开始sqlite3模块对rowcount的处理逻辑做了变更导致的——你的更新操作其实正常执行了,但rowcount的返回值和3.5及更早版本不一样了。
为什么会这样?
在Python 3.5及之前,sqlite3的rowcount直接返回SQLite内部sqlite3_changes()函数的结果,也就是实际修改的行数。但从3.6开始,为了贴合PEP 249数据库API规范,当SQLite的查询优化器跳过了某些行的扫描(比如你的语句里用到了IS NULL/IS NOT NULL这类条件,优化器可能没遍历所有匹配行),rowcount就会返回-1,哪怕实际有行被修改。
几个可行的解决方案
用SQLite原生的
changes()函数获取准确行数
这是最靠谱的办法,因为changes()是SQLite内置函数,不管Python版本如何,都能返回最后一次INSERT/UPDATE/DELETE操作实际修改的行数。修改你的代码如下:sql = """ UPDATE measurements SET value=? WHERE value IS NULL AND counter IS NOT NULL; """ with self.conn: cur = self.conn.cursor() cur.execute(sql, [value]) # 执行完更新后,立即查询changes() cur.execute("SELECT changes()") affected_rows = cur.fetchone()[0] # 现在affected_rows就是你要的受影响行数了手动对比连接的
total_changes值
如果你不想额外执行一次查询,也可以在更新前后分别获取连接的total_changes属性,差值就是本次修改的行数:sql = """ UPDATE measurements SET value=? WHERE value IS NULL AND counter IS NOT NULL; """ with self.conn: before_changes = self.conn.total_changes cur = self.conn.execute(sql, [value]) affected_rows = self.conn.total_changes - before_changes不过这个方法要注意,如果在同一个连接里还有其他操作,可能会影响结果,所以不如
changes()精准。
验证一下
我自己做了个测试,用Python 3.8跑下面的代码,rowcount返回-1,但changes()返回了正确的2行:
import sqlite3 # 内存数据库测试 conn = sqlite3.connect(':memory:') conn.execute("CREATE TABLE measurements (value INTEGER, counter INTEGER)") # 插入测试数据:2行符合更新条件,1行有值,1行counter为NULL conn.executemany("INSERT INTO measurements VALUES (?, ?)", [(None, 1), (None, 2), (3, 3), (None, None)]) conn.commit() update_value = 100 sql = "UPDATE measurements SET value=? WHERE value IS NULL AND counter IS NOT NULL;" with conn: cur = conn.execute(sql, [update_value]) print(f"rowcount返回值: {cur.rowcount}") # 输出-1 cur.execute("SELECT changes()") print(f"实际受影响行数: {cur.fetchone()[0]}") # 输出2
这样就能完美解决你的问题啦!
内容的提问来源于stack exchange,提问作者troet
相关产品推荐
相关产品推荐

