Created
July 24, 2026 14:42
-
-
Save thiagoa/4902fe5aee424a6be391c33b86e3a5b3 to your computer and use it in GitHub Desktop.
Making has_one work efficiently with a lateral join (hacky but functional)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Making has_one work efficiently with a lateral join | |
| # | |
| # The naive has_one with an ordering scope loads all matching rows | |
| # and discards all but the most recent per user in Ruby. This hack | |
| # makes the filtering happen at the SQL level. | |
| # | |
| # The trick: alias the users table as user_statuses so that Rails' | |
| # preloader filters the users table instead of the user_statuses | |
| # table before the lateral join runs. Without the aliasing trick, | |
| # Rails would filter on the join table's user_id, so it would need | |
| # to run the lateral join for every user in the table and then | |
| # discard the ones not in the filter list. | |
| class User < ApplicationRecord | |
| has_one :current_status, -> { | |
| lateral = UserStatus | |
| .from("user_statuses AS us") | |
| .where("us.user_id = user_statuses.user_id") | |
| .order("us.created_at DESC, us.id DESC") | |
| .limit(1) | |
| .select("us.*") | |
| # "user_statuses" here is actually the users table in disguise. | |
| # Rails' preloader adds WHERE "user_statuses"."user_id" IN (...) | |
| # which filters users before the lateral join runs. | |
| from("(SELECT id AS user_id FROM users) AS user_statuses") | |
| .joins("JOIN LATERAL (#{lateral.to_sql}) lateral_result ON true") | |
| .select("lateral_result.*") | |
| }, class_name: "UserStatus" | |
| end | |
| # Usage (works with eager loading): | |
| # | |
| # users = User.includes(:current_status).limit(15) | |
| # users.each { |u| u.current_status&.status } | |
| # | |
| # Why we don't recommend it: | |
| # | |
| # It's too clever and coupled to Rails internals. It works, and | |
| # it's unlikely to break, but it's not something we'd want to | |
| # maintain in a production codebase. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment