ORM vs Raw SQL When to Use Each in Your Project

Zaheer Ahmad 5 min read min read
Python
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:

  • Student is a Python class mapped to the students table.
  • session.add and session.commit automatically 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_by generates SELECT * FROM students WHERE city='Islamabad' LIMIT 1
  • session.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.

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.

To continue learning database interaction, check out these tutorials on theiqra.edu.pk:


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?

Practice the code examples from this tutorial
Open Compiler
Share this tutorial:

Test Your Python Knowledge!

Finished reading? Take a quick quiz to see how much you've learned from this tutorial.

Start Python Quiz

About Zaheer Ahmad