Ulysse BN
Ulysse BN

Reputation: 11396

Can I force the execution of an active record query chain?

I have an edge case where I want to use .first only after my SQL query has been executed.

My case is the next one:

User.select("sum((type = 'foo')::int) as foo_count",
            "sum((type = 'bar')::int) as bar_count")
    .first
    .yield_self { |r| r.bar_count / r.foo_count.to_f }

However, this would throw an SQL error saying that I should include my user_id in the GROUP BY clause. I've already found a hacky solution using to_a, but I really wonder if there is a proper way to force execution before my call to .first.

Upvotes: 3

Views: 1507

Answers (2)

tadman
tadman

Reputation: 211590

You may want to use pluck which retrieves only the data instead of select which just alters which fields get loaded into models:

User.pluck(
  "sum((type = 'foo')::int) as foo_count",
  "sum((type = 'bar')::int) as bar_count"
).map do |foo_count, bar_count|
  bar_count / foo_count.to_f 
end

You can probably do the division in the query as well if necessary.

Upvotes: 1

engineersmnky
engineersmnky

Reputation: 29328

The error is because first uses an order by statement to order by id.

"Find the first record (or first N records if a parameter is supplied). If no order is defined it will order by primary key."

Instead try take

"Gives a record (or N records if a parameter is supplied) without any implied order. The order will depend on the database implementation. If an order is supplied it will be respected."

So

User.select("sum((type = 'foo')::int) as foo_count",
        "sum((type = 'bar')::int) as bar_count")
.take
.yield_self { |r| r.bar_count / r.foo_count.to_f }

should work appropriately however as stated the order is indeterminate.

Upvotes: 2

Related Questions