ActiveRecord Basic Querying
Fast recall
Quick revision
Core answer
where, order, select, limit, and joins. Most of these return an ActiveRecord::Relation, which stores a query plan and can be refined before SQL is executed when records are actually needed. find and find_by return individual model objects with different missing-record behaviour. Hash conditions and placeholder parameters keep values bound safely; interpolating untrusted input into SQL can create SQL injection.Whether you know the chainable query methods (where/order/limit/first/find) and that they return a relation you can chain until a terminal call.
Calling .all and loading everything, or using raw SQL with interpolation where a query method is safer.
where/order/limit/first/find are chainable and return a Relation; chain until a terminal method (first, to_a, each) runs the SQL.
Interview-ready guide
Interview answer
Active Record builds database queries with chainable methods such as where, order, limit, select, and joins. Most collection methods return an ActiveRecord::Relation: a description that can be refined and passed to another method before matching rows are loaded.
Article.where(published: true).order(created_at: :desc).limit(5) expresses filtering, ordering, and limiting in one relation. Use hash conditions or placeholders such as where("price >= ?", minimum) for input values. String interpolation lets untrusted text change SQL and can create injection vulnerabilities.
A relation is usually lazy until code needs a result. Calling each, to_a, or first loads records; operations such as count, exists?, and pluck(:id) run smaller specialised queries. Calling to_a too early moves later filtering into Ruby and may load far more data than necessary.
find(id) returns a required primary-key record and raises when it is absent. find_by(slug: value) returns the first match or nil; add an order when several rows could match and the chosen row matters. Inspect to_sql and query logs when generated SQL or performance is unclear.
A good parent relation can still create N+1 queries if a view later reads unloaded associations. Load only the relationships the response uses and give pagination a stable order. Query construction should reduce database work while still returning the exact model objects or scalar values the caller needs.
ActiveRecord Basic Querying
Direct Answer
Active Record queries are built by chaining relation methods such as where, order, select, limit, and joins. Most of these return an ActiveRecord::Relation, which stores a query plan and can be refined before SQL is executed when records are actually needed. find and find_by return individual model objects with different missing-record behaviour. Hash conditions and placeholder parameters keep values bound safely; interpolating untrusted input into SQL can create SQL injection.
Deep Dive & Architecture
An ActiveRecord::Relation holds clauses and model metadata rather than immediately loading every matching row. Each chain call produces a relation with another part of the intended SQL. This lets a reusable scope contribute one condition while its caller adds pagination or ordering.
Execution boundaries differ. Iteration loads records; count, exists?, and pluck can ask the database for a smaller result; first adds a limit. Calling to_a early replaces the database's filtering options with in-memory Ruby work and can load far more rows than necessary.
Selecting columns affects the model objects returned. pluck(:id) returns plain values, which is efficient when no model behaviour is required. select(:id, :title) returns partial model instances, and reading an omitted attribute may fail, so that optimisation must stay within a narrow boundary.
Query values and SQL structure are separate concerns. Bound parameters let the database adapter quote values correctly and prevent an input string from becoming executable SQL. Dynamic column names or sort directions require allowlists because placeholders bind values, not arbitrary SQL identifiers.
Code Example
class RecentArticles
def self.for_author(author_id:, since:, limit: 5)
Article
.where(published: true, author_id: author_id)
.where("created_at >= ?", since)
.order(created_at: :desc)
.limit(limit)
end
end
recent = RecentArticles.for_author(
author_id: 4,
since: 30.days.ago,
limit: 5
)
puts recent.to_sql
recent.each { |article| puts article.title }
Common Mistakes & Pitfalls
Calling .all and loading everything, or using raw SQL with interpolation where a query method is safer.
Check your understanding
Answer these without looking back at the article.
- When does an Active Record relation execute its SQL?
- What is the difference between `select` and `pluck`?
- Why can bound values not safely replace dynamic column names?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.