Python MSSQL: How to Use SQL JOIN Statements in Python with pyodbc
Last updated 1 month ago | 112 views 75 5

Combining data from multiple tables is a fundamental part of working with relational databases. In SQL, we use JOIN
operations to bring together data that’s logically related but stored in separate tables. In this article, you’ll learn how to perform various types of SQL JOINs in Microsoft SQL Server (MSSQL) using Python and the pyodbc
library.
We’ll cover:
-
Different types of SQL JOINs
-
When and how to use each
-
Full Python examples using
pyodbc
✅ Prerequisites
Tools Required
-
Python 3.7 or later
-
Microsoft SQL Server (local or remote)
-
ODBC Driver 17 or 18 for SQL Server
Install pyodbc
pip install pyodbc
Sample Tables
To demonstrate JOIN
operations, let’s use two related tables: Employees
and Departments
.
CREATE TABLE Departments (
DepartmentID INT PRIMARY KEY,
DepartmentName NVARCHAR(100)
);
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
FirstName NVARCHAR(50),
LastName NVARCHAR(50),
DepartmentID INT FOREIGN KEY REFERENCES Departments(DepartmentID)
);
Step 1: Connect to MSSQL Using Python
import pyodbc
conn = pyodbc.connect(
"DRIVER={ODBC Driver 17 for SQL Server};"
"SERVER=localhost;"
"DATABASE=CompanyDB;"
"UID=your_username;"
"PWD=your_password;"
)
cursor = conn.cursor()
Step 2: Perform an INNER JOIN
Returns only rows with matching values in both tables.
query = """
SELECT e.FirstName, e.LastName, d.DepartmentName
FROM Employees e
INNER JOIN Departments d ON e.DepartmentID = d.DepartmentID
"""
cursor.execute(query)
for row in cursor.fetchall():
print(row.FirstName, row.LastName, '-', row.DepartmentName)
✅ Use INNER JOIN when you only want records that exist in both tables.
⬅️ LEFT JOIN: All Employees, Even Without Department
Returns all records from the left (Employees) table and matching records from the right (Departments).
query = """
SELECT e.FirstName, e.LastName, d.DepartmentName
FROM Employees e
LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID
"""
cursor.execute(query)
for row in cursor.fetchall():
print(row.FirstName, row.LastName, '-', row.DepartmentName or "No Department")
Useful for identifying orphaned records.
➡️ RIGHT JOIN: All Departments, Even Without Employees
Returns all records from the right (Departments) table and the matched records from the left (Employees).
query = """
SELECT e.FirstName, e.LastName, d.DepartmentName
FROM Employees e
RIGHT JOIN Departments d ON e.DepartmentID = d.DepartmentID
"""
cursor.execute(query)
for row in cursor.fetchall():
name = f"{row.FirstName} {row.LastName}" if row.FirstName else "No Employee"
print(name, '-', row.DepartmentName)
FULL OUTER JOIN: All Records, Matching or Not
Combines the results of LEFT and RIGHT joins.
query = """
SELECT e.FirstName, e.LastName, d.DepartmentName
FROM Employees e
FULL OUTER JOIN Departments d ON e.DepartmentID = d.DepartmentID
"""
cursor.execute(query)
for row in cursor.fetchall():
name = f"{row.FirstName} {row.LastName}" if row.FirstName else "No Employee"
dept = row.DepartmentName or "No Department"
print(name, '-', dept)
⚠️ FULL OUTER JOIN may not be supported in some legacy SQL Server versions — test it before using.
Full Example: Function with JOIN Type Parameter
def fetch_employee_department(join_type="INNER"):
join_type = join_type.upper()
if join_type not in ("INNER", "LEFT", "RIGHT", "FULL OUTER"):
raise ValueError("Unsupported JOIN type")
query = f"""
SELECT e.FirstName, e.LastName, d.DepartmentName
FROM Employees e
{join_type} JOIN Departments d ON e.DepartmentID = d.DepartmentID
"""
cursor.execute(query)
for row in cursor.fetchall():
fname = row.FirstName or "No Employee"
lname = row.LastName or ""
dname = row.DepartmentName or "No Department"
print(f"{fname} {lname} - {dname}")
# Usage
fetch_employee_department("LEFT")
Tips for Using JOINs in Python with MSSQL
Tip | Benefit |
---|---|
✅ Use table aliases (e.g., e , d ) |
Makes queries cleaner |
✅ Start with INNER JOIN |
Most common and safest |
✅ Test in SSMS before Python | Easier debugging |
✅ Handle None values in Python |
Avoid TypeError when printing |
✅ Use ORDER BY |
Helps make output predictable |
⚠️ Common Pitfalls
Problem | Cause | Fix |
---|---|---|
NULL values | Missing match in join | Handle with or in Python |
Unexpected duplicates | Bad or missing join condition | Use proper ON clause |
Performance issues | No indexes on join columns | Index foreign keys |
SQL injection risk | F-strings with user input | Avoid dynamic SQL unless necessary |
Best Practices
-
Use foreign keys to ensure relational integrity.
-
Create indexes on the columns used in joins for faster performance.
-
Prefer
INNER JOIN
unless you specifically need unmatched data. -
Use tools like ER diagrams or schema visualizers to understand table relationships.
-
Abstract repeated queries into reusable Python functions or use an ORM (like SQLAlchemy) if complexity grows.
Conclusion
SQL JOINs are essential for working with normalized databases. With pyodbc
in Python, you can easily connect to MSSQL and perform INNER
, LEFT
, RIGHT
, and FULL OUTER
joins. Always make sure your joins are logically sound, efficient, and secured against injection risks.