Python sqlite3.Cursor.execute Method
Last modified April 15, 2025
This comprehensive guide explores Python's sqlite3.Cursor.execute
method,
the primary way to execute SQL statements in SQLite databases. We'll cover basic usage,
parameter binding, transaction handling, and practical examples.
Basic Definitions
The sqlite3.Cursor.execute
method executes a single SQL statement.
It takes an SQL query as its first parameter and optional parameters for binding
values to the query.
Key characteristics: it returns the cursor object for chaining, supports parameterized queries for security, and can execute any valid SQL statement. The method is the workhorse for all database operations.
Basic SQL Execution
Here's the simplest usage of execute
to create a table and insert data.
The example uses context managers for automatic resource cleanup.
import sqlite3 with sqlite3.connect('example.db') as conn: with conn.cursor() as cursor: # Create table cursor.execute('''CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, price REAL)''') # Insert data cursor.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Laptop', 999.99)) # Commit is automatic with context manager
This example shows the basic workflow: connect to database, create cursor, execute SQL statements. The context managers ensure proper resource cleanup.
The ?
placeholders in the INSERT statement demonstrate parameterized
queries, which prevent SQL injection and handle proper value escaping.
Parameter Binding Styles
The execute
method supports different parameter binding styles. This
example shows all three supported formats.
import sqlite3 with sqlite3.connect('example.db') as conn: with conn.cursor() as cursor: # Question mark style (positional) cursor.execute("INSERT INTO products VALUES (?, ?, ?)", (2, 'Mouse', 19.99)) # Named style cursor.execute("INSERT INTO products VALUES (:id, :name, :price)", {'id': 3, 'name': 'Keyboard', 'price': 49.99}) # Numbered style cursor.execute("INSERT INTO products VALUES (?1, ?2, ?3)", (4, 'Monitor', 199.99)) conn.commit()
SQLite supports three parameter binding styles: question mark (positional), named, and numbered. Each has advantages depending on the use case.
Named parameters are especially useful with complex queries containing many parameters, as they make the code more readable and maintainable.
Executing Multiple Statements
The executescript
method allows executing multiple SQL statements
at once. This is useful for schema initialization or bulk operations.
import sqlite3 sql_script = """ BEGIN TRANSACTION; CREATE TABLE IF NOT EXISTS customers ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE ); CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY, customer_id INTEGER, amount REAL, FOREIGN KEY (customer_id) REFERENCES customers(id) ); COMMIT; """ with sqlite3.connect('shop.db') as conn: with conn.cursor() as cursor: cursor.executescript(sql_script)
This example creates a complete database schema with two related tables in a single
transaction. The executescript
method parses and executes the entire
script.
Note that executescript
commits any pending transaction before
executing and commits again after execution. It's ideal for setup scripts.
Executing Many Statements
The executemany
method efficiently executes the same SQL statement
multiple times with different parameters. This is much faster than individual
executes for bulk inserts.
import sqlite3 products = [ (5, 'Headphones', 79.99), (6, 'Webcam', 59.99), (7, 'Microphone', 89.99), (8, 'Speaker', 129.99) ] with sqlite3.connect('example.db') as conn: with conn.cursor() as cursor: cursor.executemany("INSERT INTO products VALUES (?, ?, ?)", products) print(f"Inserted {cursor.rowcount} rows")
This example inserts multiple rows in a single efficient operation. The
executemany
method is optimized for this pattern.
The rowcount
property returns the number of rows affected by the
last operation. For executemany
, it shows total affected rows.
Fetching Results
After executing a SELECT statement, you can fetch results using various methods. This example demonstrates the different fetching approaches.
import sqlite3 with sqlite3.connect('example.db') as conn: with conn.cursor() as cursor: # Execute SELECT cursor.execute("SELECT * FROM products WHERE price > ?", (50,)) # Fetch one row print("First product over $50:", cursor.fetchone()) # Fetch next 2 rows print("Next two products:", cursor.fetchmany(2)) # Fetch all remaining rows print("All remaining products:", cursor.fetchall())
The example shows three fetching methods: fetchone
for single rows,
fetchmany
for a specified number of rows, and fetchall
for all remaining rows.
The cursor maintains state about the result set, so subsequent fetch calls continue where the previous one left off. This allows efficient processing of large result sets.
Using Row Factories
Row factories allow customizing how rows are returned from queries. This example shows how to access columns by name instead of position.
import sqlite3 with sqlite3.connect('example.db') as conn: # Set row factory for named access conn.row_factory = sqlite3.Row with conn.cursor() as cursor: cursor.execute("SELECT name, price FROM products WHERE id = ?", (1,)) product = cursor.fetchone() # Access columns by name print(f"{product['name']} costs ${product['price']:.2f}") # Can still access by index print(f"Same product: {product[0]} costs ${product[1]:.2f}")
The sqlite3.Row
factory provides both index-based and name-based
access to columns. This makes code more readable and resilient to schema changes.
Row objects also support other useful features like dictionary-style key access,
keys
method, and proper string representation for debugging.
Error Handling
Proper error handling is crucial for robust database applications. This example demonstrates handling common SQLite errors during execution.
import sqlite3 try: with sqlite3.connect('example.db') as conn: with conn.cursor() as cursor: # This will fail (duplicate primary key) cursor.execute("INSERT INTO products VALUES (1, 'Tablet', 299.99)") conn.commit() except sqlite3.IntegrityError as e: print(f"Integrity error: {e}") except sqlite3.DatabaseError as e: print(f"Database error: {e}") except Exception as e: print(f"Unexpected error: {e}")
The example shows how to handle specific SQLite errors like IntegrityError
for constraint violations. More general errors can be caught with DatabaseError
.
Always handle database exceptions appropriately - at minimum log them, and often implement retry logic or user notification for recoverable errors.
Best Practices
- Use parameterized queries: Always to prevent SQL injection
- Close resources properly: Use context managers for connections/cursors
- Handle transactions explicitly: Commit or rollback as needed
- Use appropriate fetch methods: Choose based on result size needs
- Implement error handling: Catch and handle database exceptions
Source References
Author
List all Python tutorials.