ZetCode

Python sqlite3.Cursor.rowcount 属性

上次修改时间:2025 年 4 月 15 日

本综合指南探讨了 Python 的 sqlite3.Cursor.rowcount 属性,该属性返回上次操作修改的行数。我们将介绍其行为、局限性和实际使用示例。

基本定义

SQLite 游标的 rowcount 属性返回受上次执行操作影响的行数。它对于 UPDATE、DELETE 和 INSERT 语句很有用。

主要特点:它是只读的,如果没有执行任何操作或对于 SELECT 语句,则返回 -1,并且仅反映最近一次操作的影响。

基本 rowcount 示例

此示例演示了 UPDATE 操作后 rowcount 的基本用法。

basic_rowcount.py
import sqlite3

with sqlite3.connect('example.db') as conn:
    conn.execute('''CREATE TABLE IF NOT EXISTS users
                    (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)''')
    conn.execute("INSERT INTO users (name, age) VALUES ('Alice', 30)")
    conn.execute("INSERT INTO users (name, age) VALUES ('Bob', 25)")
    
    cursor = conn.cursor()
    cursor.execute("UPDATE users SET age = age + 1 WHERE name LIKE 'A%'")
    print(f"Rows updated: {cursor.rowcount}")  # Output: 1

该示例表明 rowcount 正确报告更新了 1 行。由于 WHERE 子句,UPDATE 操作仅影响了 Alice 的记录。

请注意我们如何使用上下文管理器 (with) 来自动处理连接清理,确保正确释放资源。

带有 DELETE 操作的 rowcount

此示例演示了 DELETE 操作后 rowcount 的用法。

delete_rowcount.py
import sqlite3

with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()
    
    # Setup test data
    cursor.executescript('''
        DROP TABLE IF EXISTS products;
        CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT);
        INSERT INTO products (name) VALUES ('Laptop'), ('Phone'), ('Tablet');
    ''')
    
    # Delete operation
    cursor.execute("DELETE FROM products WHERE name LIKE 'P%'")
    print(f"Rows deleted: {cursor.rowcount}")  # Output: 1

DELETE 操作仅删除“Phone”产品,因此 rowcount 返回 1。该示例还展示了如何使用 executescript 处理多个语句。

即使发生错误,上下文管理器也能确保在操作完成后正确关闭连接和游标,防止资源泄漏。

带有 INSERT 操作的 rowcount

此示例显示了 rowcount 在 INSERT 操作中的行为。

insert_rowcount.py
import sqlite3

with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS orders
                      (id INTEGER PRIMARY KEY, product TEXT, quantity INTEGER)''')
    
    # Single row insert
    cursor.execute("INSERT INTO orders (product, quantity) VALUES ('Book', 2)")
    print(f"Rows inserted (single): {cursor.rowcount}")  # Output: 1
    
    # Multiple rows insert
    cursor.executemany("INSERT INTO orders (product, quantity) VALUES (?, ?)",
                      [('Pen', 10), ('Pencil', 15)])
    print(f"Rows inserted (multiple): {cursor.rowcount}")  # Output: 1 (last operation only)

对于单个 INSERT,rowcount 正确报告 1。但是,对于 executemany,它仅报告上次操作的计数。

这演示了一个重要的限制:rowcount 不会累积单个执行调用中多个操作的计数。

带有 SELECT 语句的 rowcount

此示例演示了 rowcount 在 SELECT 语句中的行为。

select_rowcount.py
import sqlite3

with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS cities
                      (id INTEGER PRIMARY KEY, name TEXT, population INTEGER)''')
    cursor.executemany("INSERT INTO cities (name, population) VALUES (?, ?)",
                      [('New York', 8500000), ('London', 9000000)])
    
    # SELECT operation
    cursor.execute("SELECT * FROM cities WHERE population > 8000000")
    print(f"Rowcount after SELECT: {cursor.rowcount}")  # Output: -1
    
    # Verify actual row count
    rows = cursor.fetchall()
    print(f"Actual rows returned: {len(rows)}")  # Output: 2

该示例表明 rowcount 对于 SELECT 语句返回 -1。要获得实际计数,您必须获取行并检查长度。

这是 SQLite 中 rowcount 的一个关键限制 - 它不适用于 SELECT 语句,就像您可能从其他数据库系统中期望的那样。

带有事务的 rowcount

此示例演示了 rowcount 在事务中的行为。

transaction_rowcount.py
import sqlite3

with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS accounts
                      (id INTEGER PRIMARY KEY, balance INTEGER)''')
    cursor.executemany("INSERT INTO accounts (balance) VALUES (?)",
                      [(1000,), (500,)])
    
    # Start transaction
    cursor.execute("BEGIN")
    cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
    print(f"First update rowcount: {cursor.rowcount}")  # Output: 1
    
    cursor.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
    print(f"Second update rowcount: {cursor.rowcount}")  # Output: 1
    
    # Rollback transaction
    conn.rollback()
    cursor.execute("SELECT balance FROM accounts WHERE id = 1")
    print(f"Balance after rollback: {cursor.fetchone()[0]}")  # Output: 1000

该示例表明 rowcount 在事务中正常工作,报告每个操作受影响的行数。 回滚演示了 rowcount 值不受事务状态的影响。

即使更改被回滚,rowcount 值仍然正确反映了尝试的操作。

带有多个游标的 rowcount

此示例显示了 rowcount 在多个游标中的行为。

multi_cursor_rowcount.py
import sqlite3

with sqlite3.connect('example.db') as conn:
    # Create and populate table
    conn.execute('''CREATE TABLE IF NOT EXISTS inventory
                    (id INTEGER PRIMARY KEY, item TEXT, stock INTEGER)''')
    conn.execute("INSERT INTO inventory (item, stock) VALUES ('Widget', 50)")
    
    # First cursor
    cursor1 = conn.cursor()
    cursor1.execute("UPDATE inventory SET stock = stock - 10 WHERE item = 'Widget'")
    print(f"Cursor1 rowcount: {cursor1.rowcount}")  # Output: 1
    
    # Second cursor
    cursor2 = conn.cursor()
    cursor2.execute("UPDATE inventory SET stock = stock - 5 WHERE item = 'Widget'")
    print(f"Cursor2 rowcount: {cursor2.rowcount}")  # Output: 1
    
    # Verify final state
    cursor1.execute("SELECT stock FROM inventory WHERE item = 'Widget'")
    print(f"Final stock: {cursor1.fetchone()[0]}")  # Output: 35

每个游标维护自己的 rowcount 值,仅反映其最近的操作。该示例显示了通过不同游标进行的两个单独的更新。

上下文管理器确保在关闭连接时正确关闭所有游标,防止资源泄漏。

带有条件更新的 rowcount

此示例演示了 rowcount 在可能影响零行的条件更新中的用法。

conditional_rowcount.py
import sqlite3

with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS employees
                      (id INTEGER PRIMARY KEY, name TEXT, salary INTEGER)''')
    cursor.executemany("INSERT INTO employees (name, salary) VALUES (?, ?)",
                      [('John', 50000), ('Sarah', 60000)])
    
    # Update that affects rows
    cursor.execute("UPDATE employees SET salary = salary + 5000 WHERE salary < 55000")
    print(f"Rows updated (condition matched): {cursor.rowcount}")  # Output: 1
    
    # Update that affects no rows
    cursor.execute("UPDATE employees SET salary = salary + 1000 WHERE name = 'Nonexistent'")
    print(f"Rows updated (no match): {cursor.rowcount}")  # Output: 0

第一个 UPDATE 影响 John 的记录(工资 < 55000),因此 rowcount 返回 1。 第二个 UPDATE 不影响任何记录,因此返回 0。

此行为对于确定您的条件操作是否实际修改了数据库中的任何数据非常有用。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程