Python sqlite3.Cursor.fetchmany Method
Last modified April 15, 2025
This comprehensive guide explores Python's sqlite3.Cursor.fetchmany method,
used to retrieve multiple rows from a database query result. We'll cover basic usage,
parameters, memory efficiency, and practical examples.
Basic Definitions
The fetchmany method retrieves the next set of rows from a query result.
It returns a list of tuples, where each tuple represents a row from the database.
Key characteristics: it accepts a size parameter to control how many rows to fetch, is memory efficient for large result sets, and maintains cursor position between calls. It's ideal for batch processing of query results.
Basic fetchmany Usage
Here's the simplest usage of fetchmany to retrieve rows in batches.
import sqlite3
with sqlite3.connect('example.db') as conn:
conn.execute("CREATE TABLE IF NOT EXISTS products (id INTEGER, name TEXT)")
conn.executemany("INSERT INTO products VALUES (?, ?)",
[(1, 'Laptop'), (2, 'Phone'), (3, 'Tablet'),
(4, 'Monitor'), (5, 'Keyboard')])
cursor = conn.cursor()
cursor.execute("SELECT * FROM products")
# Fetch first 2 rows
batch1 = cursor.fetchmany(2)
print("Batch 1:", batch1)
# Fetch next 2 rows
batch2 = cursor.fetchmany(2)
print("Batch 2:", batch2)
# Fetch remaining rows (1 in this case)
batch3 = cursor.fetchmany(2)
print("Batch 3:", batch3)
This example shows basic batch fetching. We insert 5 rows, then fetch them in batches of 2. The cursor maintains position between calls.
The output would show the first two products, then next two, then the remaining one. The method returns an empty list when no more rows are available.
Fetching with Default Size
When no size is specified, fetchmany uses the cursor's arraysize.
import sqlite3
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
# Set the default fetch size
cursor.arraysize = 3
cursor.execute("SELECT * FROM products")
# Fetch using default size
batch = cursor.fetchmany()
print("First batch (size 3):", batch)
# Change size and fetch again
cursor.arraysize = 1
batch = cursor.fetchmany()
print("Second batch (size 1):", batch)
This demonstrates how arraysize affects fetchmany when
no size parameter is provided. The first fetch gets 3 rows, the second gets 1.
Setting arraysize is useful when you consistently want the same
fetch size throughout your application.
Processing Large Result Sets
fetchmany is ideal for processing large result sets without loading
everything into memory.
import sqlite3
def process_large_table():
with sqlite3.connect('large_db.db') as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM big_table")
while True:
batch = cursor.fetchmany(100) # Process 100 rows at a time
if not batch:
break
# Process the batch
for row in batch:
process_row(row)
def process_row(row):
# Simulate row processing
print(f"Processing row {row[0]}")
# Create sample large database
with sqlite3.connect('large_db.db') as conn:
conn.execute("CREATE TABLE big_table AS SELECT value as id, 'Data ' || value as info FROM generate_series(1, 1000)")
process_large_table()
This pattern is essential for handling large datasets. It fetches rows in manageable chunks, processing each batch before fetching the next.
The while loop continues until fetchmany returns an empty list,
indicating all rows have been processed.
Combining with fetchone
fetchmany can be combined with fetchone for flexible
result processing.
import sqlite3
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM products ORDER BY id")
# Get first row individually
first_row = cursor.fetchone()
print("First product:", first_row)
# Then get next 2 rows as a batch
next_rows = cursor.fetchmany(2)
print("Next two products:", next_rows)
# Then get one more row
another_row = cursor.fetchone()
print("Another product:", another_row)
This example shows mixing fetch styles. We get the first row with fetchone,
then a batch with fetchmany, then another single row.
The cursor maintains position consistently across all fetch methods, allowing flexible result processing.
Empty Result Handling
fetchmany gracefully handles empty result sets and partial fetches.
import sqlite3
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
# Query with no results
cursor.execute("SELECT * FROM products WHERE id = 999")
empty_result = cursor.fetchmany(5)
print("Empty result:", empty_result) # []
# Query with fewer results than requested
cursor.execute("SELECT * FROM products LIMIT 3")
partial_result = cursor.fetchmany(5)
print("Partial result (3 rows):", partial_result)
# Subsequent fetch returns empty list
next_result = cursor.fetchmany(2)
print("After exhaustion:", next_result) # []
This demonstrates fetchmany's behavior with empty or small result
sets. It returns an empty list when no rows are available.
When fewer rows exist than requested, it returns all available rows without error. Subsequent calls return empty lists.
Using with Row Factories
fetchmany works with row factories to return customized row formats.
import sqlite3
with sqlite3.connect('example.db') as conn:
# Use built-in Row factory
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute("SELECT * FROM products")
batch = cursor.fetchmany(2)
# Access rows by column name
for row in batch:
print(f"Product {row['id']}: {row['name']}")
print("Columns:", row.keys())
This example shows fetchmany with the sqlite3.Row
factory. Rows can be accessed by column name or index.
The keys method shows available column names, making the result
more self-documenting than plain tuples.
Error Handling
Proper error handling ensures robust database operations with fetchmany.
import sqlite3
try:
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
# Invalid query (no such table)
cursor.execute("SELECT * FROM non_existent_table")
batch = cursor.fetchmany(2)
except sqlite3.Error as e:
print("Database error:", e)
finally:
print("Operation attempted")
This shows handling errors that might occur during fetch operations. The
with statement ensures proper connection cleanup.
Common errors include invalid SQL, missing tables, or database access errors. Always handle these cases in production code.
Best Practices
- Use appropriate batch sizes: Balance memory and round trips
- Always check for empty results: Handle end-of-data cases
- Combine with context managers: Ensure resource cleanup
- Consider memory efficiency: Use fetchmany for large results
- Maintain cursor state: Be aware of position between calls
Source References
Author
List all Python tutorials.