13

I'm doing my best to bend my brain around arel and the relational algebra behind it, but how to represent a SELECT DISTINCT is consistently eluding my comprehension. Can anyone explain how to arel:

SELECT DISTINCT title FROM posts; 

Many thanks!

jemmons
  • 18,605
  • 8
  • 55
  • 84
  • 1
    I don't know arel but from my reading of "Database in Depth" by C.J.Date, in relational algebra the result of a query is a set of tuples. So if arel is following this theory than distinct should be the default. – Jörn Horstmann Jun 28 '10 at 21:42

6 Answers6

16

Using pure Arel (not Rails/ActiveRecord) there is a "distinct" method:

Arel::VERSION # => '3.0.2'
posts = Arel::Table.new(:posts)
posts.project(posts[:title])
posts.distinct
posts.to_sql # => 'SELECT DISTINCT "posts"."title" FROM "posts"'

Curiously, the "distinct" method is not chainable, per the other Arel methods.

maerics
  • 151,642
  • 46
  • 269
  • 291
15

The Arel way to do it is:

t = Arel::Table.new(:foo)
count_distinct = t[:field].count(true)
count_distinct.to_sql # => "COUNT(DISTINCT `foo`.`field`)"
rafb3
  • 1,694
  • 12
  • 12
  • 1
    This doesn't strictly answer the question but it does happen to answer _my_ question: how do you express `count(DISTINCT attr)` using Arel. – spume May 12 '20 at 12:34
7

The previous answer is the Rails way, no? Not the Arel way.

This works for 1.x:

posts = Table(:posts)
posts.project(Arel::Distinct.new(posts[:title]))

I'd guess there's another "more correct" way to do this via the API but I haven't figured that out yet.

Малъ Скрылевъ
  • 16,187
  • 5
  • 56
  • 69
numbers1311407
  • 33,686
  • 9
  • 90
  • 92
3

If you are doing this using a scope:

  scope :recent, lambda {|count|
    select("DISTINCT posts.*").
    joins(:whatever).
    limit(count).
    order("posts.updated_at DESC")
  }
Midwire
  • 1,090
  • 8
  • 25
1

Post.select('DISTINCT title')

Update 1:

At the time of the post, this was not available in Arel. These days, ActiveRecord::QueryMethods has the uniq method (http://apidock.com/rails/ActiveRecord/QueryMethods/uniq), so you'd want:

Post.select(:title).uniq

Update 2: Looks like Arel now supports this behavior. @maerics has the correct answer. I'd delete this if it wasn't the accepted answer.

Bryan Ash
  • 4,385
  • 3
  • 41
  • 57
0

Since AREL always uses SET in it's operation, duplicate row results will be deleted automatically. Just use a normal Project (Phi) operation.

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
Alfat Saputra Harun
  • 1,061
  • 2
  • 10
  • 18
  • This is a nice idea in theory but it is patently false in reality. Arel queries will return duplicate entries from any projection unless constrained by the "distinct" method explicitly. – maerics Sep 19 '12 at 04:50