Tous les guides
Intermédiaire-AvancéÀ votre rythmeGuide gratuitSection 14/33

Requêtes avancées et optimisation

Apprenez à construire et vérifier une application web avec des outils de développement et des assistants IA. Pratiquez les étapes de conception, code et validation.

💡

Contenu gratuit, accessible sans compte. Les outils et modèles évoluent : consultez le dossier actuel pour vérifier leurs versions. Modèles et 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')
Vérification rapide

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

Apprendre l’IA, du premier prompt aux agents

15 guides gratuitsQuiz, schémas et ateliersSans compte

Votre progression

Cette progression est déclarative et conservée dans votre navigateur. L’attestation finale ne constitue pas un diplôme ni une évaluation indépendante.

Voir ma progression et mon attestation

Apprendre l’IA, du premier prompt aux agents

15 guides gratuitsQuiz, schémas et ateliersSans compte

Un cas pratique IA dans votre boîte mail

Méthodes, ressources et nouveaux exercices. Gratuit, avec désinscription à tout moment.