All guides
Intermediate-AdvancedSelf-pacedFree GuideSection 14/33

Requêtes avancées et optimisation

Learn to build and check a web application with development tools and AI assistants. Practise design, coding and validation.

The full lessons for this course are currently available in French. Use the section menu to navigate the French course.

💡

Free content, available without an account. Tools and models change: use the current model guide to check their versions. Models and sources

Section 13.3.4 : Requêtes avancées et optimisation

🎯 Objectif pédagogique

Maîtriser les requêtes SQL avancées (subqueries, window functions) et comprendre l'optimisation des performances avec Active Record (N+1, eager loading, indices).


Le problème N+1 : l'ennemi des performances

Le problème N+1 est le piège le plus courant en Active Record. Il se produit quand vous exécutez 1 requête pour récupérer N objets, puis N requêtes supplémentaires pour leurs associations.

# BAD : N+1 queries (1 + 10 = 11 requêtes)
articles = Article.limit(10)
articles.each do |article|
  puts article.user.nom  # Chaque appel fait une requête SQL !
end

# GOOD : Eager loading (2 requêtes seulement)
articles = Article.includes(:user).limit(10)
articles.each do |article|
  puts article.user.nom  # Déjà chargé en mémoire
end

Eager loading : includes, preload, eager_load

# includes : utilise preload ou eager_load selon le cas
Article.includes(:user, :tags).where(published: true)

# preload : 2+ requêtes séparées
Article.preload(:user).limit(10)
# SELECT * FROM articles LIMIT 10
# SELECT * FROM users WHERE id IN (1, 2, 3, ...)

# eager_load : LEFT OUTER JOIN (une seule requête)
Article.eager_load(:user).where(users: { ville: 'Paris' })
# SELECT articles.*, users.* FROM articles
# LEFT OUTER JOIN users ON users.id = articles.user_id
# WHERE users.ville = 'Paris'

Scopes : requêtes réutilisables

class Article < ApplicationRecord
  scope :published, -> { where(published: true) }
  scope :recent, -> { order(created_at: :desc) }
  scope :by_city, ->(city) { joins(:user).where(users: { ville: city }) }
  scope :popular, -> { where('views_count > ?', 100) }

  # Equivalent à des méthodes de classe
  def self.trending
    published.recent.popular.limit(10)
  end
end

# Chaînable
Article.published.recent.limit(5)
Article.by_city('Paris').published
Article.trending

Indices : accélérer les requêtes

Un index est comme l'index d'un livre — il permet à la base de trouver rapidement une ligne sans scanner toute la table.

class AddIndicesToArticles < ActiveRecord::Migration[7.1]
  def change
    add_index :articles, :user_id           # Foreign key
    add_index :articles, :published          # Colonnes fréquemment filtrées
    add_index :articles, :created_at         # Colonnes triées
    add_index :articles, [:user_id, :published]  # Index composite
    add_index :users, :email, unique: true   # Index unique
  end
end

Subqueries et SQL avancé

# Subquery : trouver les users qui ont au moins un article
User.where(id: Article.select(:user_id).distinct)

# SQL brut quand nécessaire
User.find_by_sql("SELECT users.*, COUNT(articles.id) as articles_count
                   FROM users
                   LEFT JOIN articles ON articles.user_id = users.id
                   GROUP BY users.id
                   HAVING COUNT(articles.id) > 5")

# select pour ajouter des colonnes calculées
User.select('users.*, COUNT(articles.id) as articles_count')
    .joins(:articles)
    .group('users.id')
    .order('articles_count DESC')
Quick Check

Comment résoudre un problème N+1 en Active Record ?

Learn AI, from prompts to agents

15 free guidesQuizzes, diagrams and workshopsNo account needed

Your progress

Progress is self-reported and stored in your browser. The final completion record is not a qualification or an independent assessment.

View progress and completion record

Learn AI, from prompts to agents

15 free guidesQuizzes, diagrams and workshopsNo account needed

One practical AI workflow in your inbox

Methods, resources and new exercises. Free, unsubscribe at any time.