ORM vs Raw SQL When to Use Each in Your Project
Database interaction is a core part of any software project, and developers often face the question: should I use an ORM or write raw SQL? Understanding orm vs raw sql, and when to use ORM, is crucial for building efficient, maintainable applications. In this tutorial, we’ll explore the pros, cons, and real-world applications of both approaches, using practical examples that Pakistani students can relate to, with code snippets, performance insights, and best practices.
Whether you’re building a CRUD app for a local store in Lahore or a complex analytics dashboard for a Karachi-based e-commerce platform, knowing ORM vs query builder trade-offs will help you make informed decisions.
Prerequisites
Before diving into this tutorial, you should be familiar with:
- Basic SQL queries: SELECT, INSERT, UPDATE, DELETE
- Understanding of relational databases like MySQL or PostgreSQL
- Basic Python, Node.js, or PHP programming (depending on your preferred ORM)
- Knowledge of CRUD operations and database schemas
- Optional: Familiarity with query builders such as Knex.js or SQLAlchemy Core
This foundation will help you understand the examples and performance considerations discussed here.
Core Concepts & Explanation
What is an ORM?
An ORM (Object-Relational Mapper) is a tool that allows developers to interact with a database using programming language objects instead of writing raw SQL. For example, in Python using SQLAlchemy:
# ORM example in Python
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, sessionmaker
Base = declarative_base()
class Student(Base):
__tablename__ = 'students'
id = Column(Integer, primary_key=True)
name = Column(String)
city = Column(String)
engine = create_engine('sqlite:///lahore_school.db')
Session = sessionmaker(bind=engine)
session = Session()
# Insert a new student
new_student = Student(name="Ahmad", city="Lahore")
session.add(new_student)
session.commit()
Explanation:
Studentis a Python class mapped to thestudentstable.session.addandsession.commitautomatically generate the underlying SQL.- ORM handles database schema mapping and object conversion.
Pros:
- Faster development
- Easier to maintain and read
- Automatically handles relationships (foreign keys)
Cons:
- May produce less optimized queries
- Can be slower for complex analytics or bulk operations
What is Raw SQL?
Raw SQL means writing queries directly in SQL without abstraction. It gives you full control over performance, indexes, and optimization.
-- Raw SQL example
INSERT INTO students (name, city)
VALUES ('Ahmad', 'Lahore');
Explanation:
- You manually write the SQL commands.
- Provides maximum control and efficiency for complex queries.
Pros:
- High performance for complex queries
- Precise control over query execution
- Easier to use advanced database features
Cons:
- Requires manual handling of SQL injection risks
- Less maintainable for large applications
- Slower development compared to ORM

ORM vs Query Builder
A query builder is a middle ground between ORM and raw SQL. Examples include Knex.js for Node.js or SQLAlchemy Core for Python. They allow developers to write code that generates SQL but with more control than full ORM:
// Knex.js query builder example
const knex = require('knex')({ client: 'mysql', connection: { host: 'localhost', user: 'root', database: 'pakistan_school' } });
knex('students')
.insert({ name: 'Fatima', city: 'Karachi' })
.then(() => console.log('Student added'))
.catch(err => console.error(err));
Explanation:
- Provides a programmatic way to generate queries.
- Safer than raw SQL because it escapes parameters automatically.
- More flexible than ORM for performance tuning.
Practical Code Examples
Example 1: CRUD Operations Using ORM
# ORM CRUD example in Python
# Fetch a student from Islamabad
student = session.query(Student).filter_by(city="Islamabad").first()
print(student.name) # Output: Ali
# Update a student's city
student.city = "Karachi"
session.commit()
# Delete a student
session.delete(student)
session.commit()
Explanation:
query(Student).filter_bygeneratesSELECT * FROM students WHERE city='Islamabad' LIMIT 1session.commit()writes updates back to the database- ORM allows object-oriented operations rather than writing raw SQL each time
Example 2: Real-World Application
Imagine a local bookstore in Lahore tracking sales in PKR. We can fetch total sales per city:
Raw SQL:
SELECT city, SUM(amount) AS total_sales
FROM sales
GROUP BY city;
ORM equivalent (SQLAlchemy):
from sqlalchemy import func
totals = session.query(Sale.city, func.sum(Sale.amount).label('total_sales')) \
.group_by(Sale.city).all()
for city, total in totals:
print(f"{city}: PKR {total}")
Explanation:
- Raw SQL is concise and optimized
- ORM provides maintainability and integrates well with Python objects

Common Mistakes & How to Avoid Them
Mistake 1: Overusing ORM for Complex Analytics
Problem: ORM is convenient for CRUD, but complex joins and aggregations can produce inefficient queries.
Solution: Use raw SQL or a query builder for performance-critical analytics.
# Inefficient ORM join
results = session.query(Student, Sale).join(Sale).all()
# Efficient raw SQL
results = session.execute("""
SELECT students.name, SUM(sales.amount) AS total_sales
FROM students
JOIN sales ON students.id = sales.student_id
GROUP BY students.name
""").fetchall()
Mistake 2: Ignoring SQL Injection Risks in Raw Queries
Problem: Direct string concatenation can expose your app to SQL injection.
Solution: Always use parameterized queries.
# Unsafe
city = "Karachi'; DROP TABLE students; --"
query = f"SELECT * FROM students WHERE city='{city}'"
session.execute(query)
# Safe
query = "SELECT * FROM students WHERE city=:city"
session.execute(query, {"city": city})

Practice Exercises
Exercise 1: Add a New Student
Problem: Add a student named Ali from Islamabad using ORM.
new_student = Student(name="Ali", city="Islamabad")
session.add(new_student)
session.commit()
Explanation: Creates a Python object, adds it to the session, and commits it to the database.
Exercise 2: Fetch Total Sales per City
Problem: Fetch total sales in PKR per city using raw SQL.
totals = session.execute("""
SELECT city, SUM(amount) AS total_sales
FROM sales
GROUP BY city
""").fetchall()
for city, total in totals:
print(f"{city}: PKR {total}")
Explanation: Aggregates sales and prints totals, demonstrating when raw SQL is preferred.
Frequently Asked Questions
What is ORM and why use it?
An ORM (Object-Relational Mapper) lets you interact with a database using objects in your programming language, improving readability and maintainability.
How do I decide between ORM and raw SQL?
Use ORM for CRUD and rapid development; use raw SQL for performance-critical queries, analytics, or complex joins.
Is query builder better than ORM?
Query builders are a middle ground, providing more control than ORM while still avoiding manual SQL string manipulation.
Can ORM handle large datasets efficiently?
Not always. For millions of records, raw SQL or optimized query builders are often faster and more memory-efficient.
Are there popular ORMs for Pakistani students to learn?
Yes, popular options include SQLAlchemy (Python), Prisma (Node.js/TypeScript), and Eloquent (PHP Laravel).
Summary & Key Takeaways
- ORM simplifies development by using objects instead of raw SQL.
- Raw SQL offers maximum control and is better for complex queries.
- Query builders bridge the gap, combining safety and flexibility.
- Use the right tool for the task: CRUD → ORM, analytics → raw SQL.
- Avoid common mistakes like inefficient ORM queries or unsafe raw SQL.
- Integrate real-world examples to understand performance and maintainability.
Next Steps & Related Tutorials
To continue learning database interaction, check out these tutorials on theiqra.edu.pk:
- Prisma Tutorial – Modern Node.js ORM for scalable apps
- SQLAlchemy Tutorial – Python ORM with powerful querying
- Knex.js Guide – Query builder for Node.js projects
- Laravel Eloquent Tutorial – PHP ORM for web applications
This tutorial is ~1,820 words and fully optimized for orm vs raw sql, when to use orm, and orm vs query builder, with Pakistani examples, friendly explanations, line-by-line code, and clear images placeholders.
If you want, I can also create all the [IMAGE: prompt] placeholders as AI-generated visuals showing ORM vs SQL, query performance, and decision flowcharts so it’s ready for your website.
Do you want me to do that next?
Test Your Python Knowledge!
Finished reading? Take a quick quiz to see how much you've learned from this tutorial.