On this page
  1. What does an ORM do?
  2. What does an ORM hide from you?
  3. When should I use raw SQL instead?
  4. Where AI coding assistants get this wrong
  5. Checklist
  6. FAQ
    1. Is using an ORM a security risk?
    2. Which ORM should I learn?
  7. Related topics
  8. Sources
concept

What Is an ORM (and When Should You Use Raw SQL)?

An ORM maps database tables to objects so you write queries in your language. Learn what it hides, what it costs, and when raw SQL is the right call.

Quick answer

  • An ORM (object-relational mapper) lets you work with database rows as objects in your programming language instead of writing SQL.
  • It speeds up development and prevents common SQL errors, but it can hide what’s actually running — including slow or surprising queries.
  • Use the ORM for everyday CRUD; reach for raw SQL for complex queries, bulk operations, and anything where performance matters.

What does an ORM do?

An ORM maps tables to classes and rows to objects. Instead of SELECT * FROM users WHERE id = 5, you write User.get(5) (or similar) and the ORM generates and runs the SQL for you. It also handles relationships (a user’s orders load through an attribute), object-to-row conversions, and often schema definition. The value is development speed and type safety: the ORM catches column-name typos at compile time instead of failing at runtime.

What does an ORM hide from you?

The actual SQL. That’s convenient and dangerous at once: the ORM’s generated query can be dramatically slower than one you’d write by hand — the classic N+1 problem, where loading 100 users fires 101 queries (one per user’s orders) because the ORM lazy-loads relationships. ORMs also make it easy to load entire tables into memory, or generate deeply nested joins you never intended. The fix isn’t abandoning the ORM; it’s knowing when to use its eager-loading and raw-query escapes.

When should I use raw SQL instead?

When the query is complex or performance-critical: reports, aggregations, bulk updates, recursive queries, window functions. A hand-written query is shorter, more readable, and often orders of magnitude faster than the ORM’s generated equivalent. The practical pattern: ORM for CRUD, raw SQL for queries that matter, and the database’s EXPLAIN command to check whether either approach is using your indexes. Both styles can coexist in the same codebase.

# ORM: everyday CRUD\nuser = session.get(User, 5)\n\n# Raw SQL: a report the ORM would mangle\nrows = session.execute(text('''\n  SELECT date_trunc('day', created_at) AS day, count(*)\n  FROM orders GROUP BY day ORDER BY day\n''')).all()

Where this bites vibecoders

AI assistants default to ORMs, and the generated code works — until a page that loads ‘all the things’ fires hundreds of queries and the database melts. The assistant never sees the query log, so the N+1 and full-table loads ship silently. The habit that fixes this class: after generating any ORM code that touches relationships, check the query count (logging or a tool like Django Debug Toolbar) and ask the assistant for eager loading or a raw query where it matters.

Where AI coding assistants get this wrong

  • Lazy-loading relationships in loops, generating the N+1 query storm.
  • Loading entire tables with .all() when a filter would do.
  • Writing reports with the ORM when raw SQL would be shorter and faster.
  • Adding indexes without checking whether the ORM’s generated queries actually use them.

Checklist

  • Use the ORM for CRUD and relationships; use raw SQL for complex queries and reports.
  • Eager-load relationships you know you’ll access.
  • Check query counts and use EXPLAIN on anything slow.
  • Prefer ORM pagination and limits over full-table loads.

FAQ

Is using an ORM a security risk?

Used properly, no — ORMs parameterize queries, which prevents SQL injection. The risk appears when you bypass the ORM with string-built raw SQL or raw f-strings. Keep raw queries parameterized and the ORM handles the rest.

Which ORM should I learn?

The one your framework uses: SQLAlchemy or Django ORM in Python, Prisma or Sequelize in Node, ActiveRecord in Rails, EF Core in .NET. They’re conceptually similar — learn the concepts (mapping, relationships, eager loading) and the specifics transfer.

Sources

Share: