Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return every nth row from database using ActiveRecord in rails

Ruby 1.9.2 / rails 3.1 / deploy onto heroku --> posgresql

Hi, Once a number of rows relating to an object goes over a certain amount, I wish to pull back every nth row instead. It's simply because the rows are used (in part) to display data for graphing, so once the number of rows returned goes above say 20, it's good to return every second one, and so forth.

This question seemed to point in the right direction: ActiveRecord Find - Skipping Records or Getting Every Nth Record

Doing a mod on row number makes sense, but using basically:

@widgetstats = self.widgetstats.find(:all,:conditions => 'MOD(ROW_NUMBER(),3) = 0 ')

doesn't work, it returns an error:

PGError: ERROR:  window function call requires an OVER clause

And any attempt to solve that with e.g. basing my OVER clause syntax on things I see in the answer on this question:

Row numbering in PostgreSQL

ends in syntax errors and I can't get a result.

Am I missing a more obvious way of efficiently returning every nth task or if I'm on the right track any pointers on the way to go? Obviously returning all the data and fixing it in rails afterwards is possible, but terribly inefficient.

Thank you!

like image 756
Dave Avatar asked Nov 23 '11 23:11

Dave


2 Answers

I think you are looking for a query like this one:

SELECT * FROM (SELECT widgetstats.*, row_number() OVER () AS rownum FROM widgetstats ORDER BY id) stats WHERE mod(rownum,3) = 0

This is difficult to build using ActiveRecord, so you might be forced to do something like:

@widgetstats = self.widgetstats.find_by_sql(
  %{
    SELECT * FROM
    (
      SELECT widgetstats.*, row_number() OVER () AS rownum FROM widgetstats ORDER BY id
    ) AS stats
    WHERE mod(rownum,3) = 0
  }
)

You'll obviously want to change the ordering used and add any WHERE clauses or other modifications to suit your needs.

like image 182
Carl Zulauf Avatar answered Sep 19 '22 22:09

Carl Zulauf


Were I to solve this, I would either just write the SQL myself, like the SQL that you linked to. You can do this with

my_model.connection.execute('...')

or just get the id numbers and find by id

ids = (1..30).step(2)
my_model.where(id => ids)
like image 20
John Hinnegan Avatar answered Sep 17 '22 22:09

John Hinnegan