Hello! Welcome to the final lesson of our "Database Systems and SQL" module.
In our previous lessons, you mastered the art of database design, culminating in normalizing schemas to 3NF. This theoretical foundation is crucial for building efficient and reliable databases. Now, it's time to bridge the gap between design and application. In data science, you rarely just design databases; you actively interact with them to extract, manipulate, and analyze data.
This lesson will teach you how to connect to an SQL database from Python to execute queries and fetch data. We'll use Python's built-in sqlite3 library, which is a fantastic tool for learning because it doesn't require any complex server setup. You'll learn the complete workflow: from establishing a connection to fetching query results directly into a Pandas DataFrame, a core skill for any data scientist.
The Standard Workflow: Python and sqlite3
Interacting with an SQLite database from Python follows a standard, logical sequence of steps. Understanding this workflow is key to managing any database connection in your code.
- Import: Bring the necessary library into your script with
import sqlite3. - Connect: Establish a connection to a database file. If the file doesn't exist, Python will create it for you.
- Cursor: Create a cursor object. Think of the cursor as a controller that allows you to execute commands and navigate through the results.
- Execute: Use the cursor to run your SQL commands (e.g.,
CREATE TABLE,INSERT,SELECT). - Commit/Fetch:
- For data-modifying commands (
INSERT,UPDATE,DELETE), you must commit the transaction to save your changes. - For data-retrieval commands (
SELECT), you fetch the results from the cursor.
- For data-modifying commands (
- Close: Close the connection to release the database file and its resources.
Let's see this entire process in action.
Python SQLite Tutorial: Complete Overview - Creating a Database, Table, and Running Queries
This excellent video from Corey Schafer provides a complete walkthrough of using sqlite3 in Python. We'll use it to guide us through the fundamental operations.
Please watch from the beginning up to 07:40. This segment covers the essential first steps: Importing sqlite3 and Connecting (01:57 - 03:47): Notice how sqlite3.connect('employee.db') creates a connection and the database file itself. Creating a Cursor and Executing a CREATE TABLE command (03:47 - 06:47): Pay close attention to how a multi-line SQL query is written and executed using c.execute(). Committing Changes and Closing the Connection (06:36 - 07:40): Understand the importance of conn.commit() to save changes and conn.close() as a best practice.
To recap the core Python code for these initial steps:
import sqlite3
# 1. Connect to a database (or create it)
conn = sqlite3.connect('mydatabase.db')
# 2. Create a cursor object
c = conn.cursor()
# 3. Execute a command (e.g., create a table)
c.execute("""CREATE TABLE employees (
first text,
last text,
pay integer
)""")
# 4. Commit the command to save the changes
conn.commit()
# 5. Close the connection
conn.close()
Writing and Reading Data
Now that we have a table, let's perform the two most common operations: inserting data into it and reading data from it.
Inserting Data and a Crucial Security Note
You can insert data using the INSERT INTO command. However, it's critically important to do this safely. A common mistake for beginners is to use Python's string formatting to insert variables into a query. This is extremely dangerous and makes your code vulnerable to SQL injection attacks.
The correct method is to use placeholders (?). The execute method will safely substitute the placeholder with your variable's value.
Python SQLite Tutorial: Complete Overview - Creating a Database, Table, and Running Queries
Let's continue with the same video. This next part covers inserting data and, most importantly, the safe way to do it.
Please watch the following two segments: Simple Insert (07:40 - 08:43): This shows a basic INSERT statement. Safe Insertion with Placeholders (15:21 - 21:07): This is one of the most important parts of the lesson. Understand why direct string formatting is bad practice and how to use ? placeholders with a tuple of values. This is essential for writing secure code.
Here is a comparison of the wrong way and the right way:
# Assume conn and c are already created
emp_first = 'John'
emp_last = 'Doe'
# WRONG and DANGEROUS way (vulnerable to SQL injection)
# c.execute(f"SELECT * FROM employees WHERE first = '{emp_first}'")
# RIGHT and SAFE way (using placeholders)
c.execute("SELECT * FROM employees WHERE first = ?", (emp_first,))
Notice that when passing a single variable, it must still be in a tuple, hence the comma (emp_first,).
Reading Data with SELECT
After executing a SELECT query, the results are held in the cursor object. You need to explicitly fetch them.
There are three ways to do this:
c.fetchone(): Fetches the next single row from the results. ReturnsNoneif no more rows are available.c.fetchmany(size): Fetches the nextsizenumber of rows as a list.c.fetchall(): Fetches all remaining rows from the results as a list of tuples.
Python SQLite Tutorial: Complete Overview - Creating a Database, Table, and Running Queries
Now, let's see how to retrieve the data we've inserted.
Watch from 08:43 to 12:05. This part demonstrates: Executing a SELECT statement. Using fetchone() and fetchall() to get the data. The format of the returned data (a tuple for one row, a list of tuples for multiple rows).
A common pattern is to fetch all results and loop through them:
c.execute("SELECT * FROM employees")
# Fetch all rows into a list
results = c.fetchall()
# Loop through the list and print each row
for row in results:
print(row)
# ('Corey', 'Schafer', 50000)
# ('Mary', 'Schafer', 70000)
A More "Pythonic" Way: The with Statement
Remembering to commit your changes and close the connection can be tedious. Python offers a cleaner, safer way to handle this using a context manager (the with statement). When you use with on a connection object, it will automatically commit your transaction if the code block completes successfully, or roll it back (undo it) if an error occurs.
This is a highly recommended practice.
Python SQLite Tutorial: Complete Overview - Creating a Database, Table, and Running Queries
Let's look at a more robust and modern way to handle database transactions.
Watch the short segment from 24:01 to 25:09. It explains how the with statement can automatically handle commits and rollbacks, simplifying your code.
Here's how it looks:
# The 'with' statement handles the commit and closing
with sqlite3.connect('mydatabase.db') as conn:
c = conn.cursor()
# No need for conn.commit() or conn.close() later
c.execute("DELETE FROM employees WHERE last = ?", ('Doe',))
Test your understanding!
Write a complete Python script that:
- Creates a database named
library.db. - Creates a table named
bookswith three columns:title(text),author(text), andyear(integer). - Safely inserts the following book:
('The Hitchhiker\'s Guide to the Galaxy', 'Douglas Adams', 1979). - Selects all books from the table and prints them to the console.
- Uses the
withstatement for managing the connection.
Show answer
import sqlite3
# Data to be inserted
book_data = ('The Hitchhiker\'s Guide to the Galaxy', 'Douglas Adams', 1979)
# Use a 'with' statement for automatic commit/close
try:
with sqlite3.connect('library.db') as conn:
c = conn.cursor()
# Create table (it's safe to run this even if it exists, using IF NOT EXISTS)
c.execute("""CREATE TABLE IF NOT EXISTS books (
title text,
author text,
year integer
)""")
# Insert a row of data safely
c.execute("INSERT INTO books VALUES (?, ?, ?)", book_data)
print("Book inserted successfully.")
# Select all data and print
print("\nAll books in the library:")
c.execute("SELECT * FROM books")
all_books = c.fetchall()
for book in all_books:
print(book)
except sqlite3.Error as e:
print(f"Database error: {e}")
Note: Running this script multiple times will insert the same book repeatedly. A more advanced design would use primary keys to prevent duplicates.
Bridging to Data Science: Integrating Pandas
While fetchall() is useful, for data analysis you'll almost always want your data in a Pandas DataFrame. The pandas library integrates beautifully with sqlite3, allowing you to read the results of an SQL query directly into a DataFrame.
Accessing SQLite Databases Using Python and Pandas
This resource from Data Carpentry shows the most direct way to get SQL data into a Pandas DataFrame, a fundamental skill for data science.
Please read the section titled 'Accessing data stored in SQLite using Python and Pandas'. Focus on the pd.read_sql_query() function. It's a powerful shortcut that combines connecting, executing, and fetching into one command.
The pd.read_sql_query() function is incredibly efficient. It takes an SQL query string and a connection object, and returns a fully populated DataFrame.
Here's an example:
import pandas as pd
import sqlite3
# Establish the connection
conn = sqlite3.connect('mydatabase.db')
# Define your SQL query
query = "SELECT first, last, pay FROM employees WHERE pay > 60000"
# Use pandas to execute the query and load data into a DataFrame
df = pd.read_sql_query(query, conn)
# Don't forget to close the connection
conn.close()
# Now you can use all the power of Pandas!
print(df.head())
print("\nAverage pay for high earners:", df['pay'].mean())
You can also write a DataFrame back to a new SQL table using the df.to_sql() method, which is very useful for saving processed data.
Conclusion
Congratulations on completing the "Database Systems and SQL" module! You've journeyed from the theoretical underpinnings of relational databases and normalization to the practical application of interacting with a database using Python.
Key Takeaways:
- The
sqlite3module provides a simple, file-based database system built into Python. - The standard workflow is: Connect -> Cursor -> Execute -> Commit/Fetch -> Close.
- Using the
withstatement is a best practice for automatically managing commits and closing connections. - Always use placeholders (
?) in yourexecutecalls to prevent SQL injection vulnerabilities. - The
pandas.read_sql_query()function is the most effective way to pull data from a database directly into a DataFrame for analysis.
Preview of the Next Lesson:
With your new skills in data retrieval and manipulation using Python, SQL, and Pandas, you are perfectly positioned to start the next phase of your journey. The next module, "Foundations of Machine Learning," begins with a lesson where you will learn to distinguish between supervised, unsupervised, and reinforcement learning. The data you've just learned to query will be the fuel for the models you're about to build.