Practice retrieving database records using fetchone(), fetchall(), fetchmany(), dictionary cursors, and SQLAlchemy result proxies interactively inside Google Colab.
When executing a SELECT query in Python, MySQL returns a dataset proxy that can be consumed in multiple ways. Depending on memory constraints and application logic, you can retrieve data line-by-line with fetchone(), in fixed-size chunks with fetchmany(), or all at once with fetchall().
We defined my_conn in our database connection guide and rely on the sample student table.
The fetchone() method fetches the single next available row from the active result set. If no more rows are present, it returns None.
my_cursor = my_conn.cursor()
my_cursor.execute("SELECT * FROM student")
my_result = my_cursor.fetchone() # Returns a single tuple
# Print full tuple
print(my_result)
# Print each column value on a new line
for x in my_result:
print(x)
The returned row is structured as a tuple. We can verify its data type using type(my_result):
print(type(my_result)) # Output: <class 'tuple'>
Output for the first record in the student table:
(1, 'John Deo', 'Four', 75, 'female')
1
John Deo
Four
75
female
Because the result is a tuple, individual fields can be accessed via zero-based integer indices matching the table schema:
my_cursor = my_conn.cursor()
my_cursor.execute("SELECT * FROM student")
my_result = my_cursor.fetchone()
print("Student id =", my_result[0])
print("Student Name =", my_result[1])
print("Student Class =", my_result[2])
print("Student Mark =", my_result[3])
print("Student Gender =", my_result[4])
Output:
Student id = 1
Student Name = John Deo
Student Class = Four
Student Mark = 75
Student Gender = female
To process records line-by-line until the result set is exhausted, use a while loop checking for None:
my_cursor = my_conn.cursor()
my_cursor.execute("SELECT * FROM student")
my_result = my_cursor.fetchone()
while my_result is not None:
print(my_result)
my_result = my_cursor.fetchone()
Instead of manually invoking fetchone() inside a loop, Python cursors are iterators. You can loop directly over the cursor object:
my_cursor = my_conn.cursor()
my_cursor.execute("SELECT * FROM student")
for my_result in my_cursor:
print(my_result)
The fetchall() method retrieves all remaining rows of a query result set as a list of tuples.
my_cursor = my_conn.cursor()
my_cursor.execute("SELECT * FROM student")
my_result = my_cursor.fetchall()
for row in my_result:
print(row)
You can transform fetched tuples into standalone Python lists for analytics or dropdown generation using list comprehensions:
q = "SELECT id, name FROM student WHERE class='Four'"
my_cursor.execute(q)
my_result = my_cursor.fetchall()
my_ids = [row[0] for row in my_result] # List of IDs
my_names = [row[1] for row in my_result] # List of names
print(my_ids)
print(my_names)
To inspect active column names dynamically, use cursor metadata keys or column descriptions:
q = "SELECT id, name, class, mark, gender FROM student WHERE class='Four'"
my_cursor.execute(q)
# Fetch column headers from cursor description
columns = [desc[0] for desc in my_cursor.description]
print(columns) # Output: ['id', 'name', 'class', 'mark', 'gender']
The fetchmany(size) method collects a specified number of rows. This is essential for processing large datasets in memory-efficient batches.
my_cursor = my_conn.cursor()
my_cursor.execute("SELECT * FROM student")
my_result_top = my_cursor.fetchmany(size=3)
for row in my_result_top:
print(row)
Output:
(1, 'John Deo', 'Four', 75, 'female')
(2, 'Max Ruin', 'Three', 85, 'male')
(3, 'Arnold', 'Three', 55, 'male')
size parameter in fetchmany(), it defaults to returning only 1 row.
When you call fetchmany() followed by fetchall(), the cursor resumes reading from where the previous fetch stopped:
my_cursor = my_conn.cursor()
my_cursor.execute("SELECT * FROM student")
# Fetch first 3 records
my_result_top = my_cursor.fetchmany(size=3)
print("--- Top 3 Records ---")
for row in my_result_top:
print(row)
# Fetch remaining records from record #4 onward
my_result = my_cursor.fetchall()
print("--- Remaining Records ---")
for row in my_result:
print(row)
Memory Insight: Notice that we do not re-execute the SQL statement. After execution, rows are stored in client buffer memory, and calls to fetchmany() or fetchall() simply consume records sequentially from that buffer.
Filtering rows in Python after running a full SELECT * FROM table statement wastes network bandwidth and memory. Always use SQL clauses (LIMIT, WHERE) to restrict data at the database level before fetching:
# Preferred: Database returns only 10 rows
my_cursor.execute("SELECT * FROM student LIMIT 0, 10")
my_result = my_cursor.fetchall()
To convert raw SQL result tuples into dictionary maps indexed by primary key or column names, use modern SQLAlchemy connection contexts:
from sqlalchemy import create_engine, text
engine = create_engine("mysql+pymysql://demo_user:demo_password@localhost/my_db")
query = "SELECT id, name, class, mark, gender FROM student LIMIT 0, 5"
my_dict = {}
my_list = []
with engine.connect() as conn:
result = conn.execute(text(query))
for row in result:
# row is a Row object supporting tuple unpacking and key indexing
my_dict[row.id] = row
my_list.append(row.name)
print("Dictionary by ID:", my_dict)
print("Name List:", my_list)
# Look up record details for 'Arnold'
for student_id, student_data in my_dict.items():
if student_data.name == "Arnold":
print(f"Found Arnold: ID={student_id}, Class={student_data.class}, Gender={student_data.gender}")
WHERE clause matches no records, or all rows in the active result set have already been consumed by a previous fetchone(), fetchmany(), or loop iteration. To read data again, re-execute the query.
SELECT, MySQL Connector throws an Unread result found exception. Solve this by calling fetchall() or instantiating a buffered cursor (buffered=True).
mysql.connector, instantiate a dictionary cursor: cursor = my_conn.cursor(dictionary=True). In pymysql, use cursor = my_conn.cursor(pymysql.cursors.DictCursor). This formats each returned row as a key-value dictionary using column names as keys.
fetchall() on massive tables because it loads all records into Python RAM simultaneously. Use unbuffered cursor iteration or batch processing via fetchmany(1000) combined with server-side pagination (SQL LIMIT / OFFSET).
Learn how to establish database connections using MySQL Connector, PyMySQL, and SQLAlchemy.
Understand how row counts behave across SELECT statements, buffered cursors, and data mutation queries.
Use pd.read_sql() to read MySQL database tables directly into Pandas DataFrames for data science.
Download the complete student.sql database dump file containing sample records with the updated gender column schema.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.