4

I'm attempting to put together some basic report screens. I've got some fairly complicated SQL queries that I'm feeding into ActiveRecord's find_by_sql method. The problem I am having here is that I am losing the order of the columns as given in the original query. I'm assuming that this is because the Hash class does not preserve entry order of its keys.

Is there a way around this problem? Should I be using a different method then find_by_sql for my queries?

Christoph Schiessl
  • 6,818
  • 4
  • 33
  • 45
Jeff Waltzer
  • 532
  • 5
  • 12

4 Answers4

3

I like to use Ruport for reporting. It has good ActiveRecord integration and it enables you to control column order and pretty much anything else. And it's sufficiently simple to use that I don't consider it overkill even for "basic" reports.

iconoclast
  • 21,213
  • 15
  • 102
  • 138
Avdi
  • 18,340
  • 6
  • 53
  • 62
2

You're correct in that the Ruby Hash does not preserve order. That's part of the point, really - you access it using the key.

I assume your query is written to deliver the columns in the order that you want to output them and you were hoping to output the values via a loop? Seems like a decent enough idea, but I can't think of a way to achieve it without at least some extra work.

What I'd recommend is to explicitly access the columns by key in your template, since you're probably going to end up applying styles, formatting using helper functions like number_with_delimiter, that kind of thing.

To get something like the shortcut mentioned above, I suppose you could create an array of symbols in the order required and pull the values out of the hash in a loop. Something like this? (please excuse the potentially dodgy erb: I'm a haml user!)

<% for row in @report.rows %>
    <tr>
    <% for col in [:a, :b, :c] %>
        <td><%= row[col] %></td>
    <% end %>
    </tr>
<% end %>
Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
  • This is what I ended up doing. We have a number of SQL queries for basic reports. What I wanted to do was to set things up so I wouldn't have to create separate controller/views for each SQL 'report' (the page is the same except for the data populating it). – Jeff Waltzer Oct 16 '08 at 20:18
  • 1
    the hash retains the order in ruby 1.9 i think – Scott Schulthess Jul 13 '10 at 13:08
1

In rails 3.2 and higher you can use attribute_names for each record of find_by_sql results. This is documented in find_by_sql:

Executes a custom SQL query against your database and returns all the results. The results will be returned as an array with columns requested encapsulated as attributes of the model you call this method from. If you call Product.find_by_sql then the results will be returned in a Product object with the attributes you specified in the SQL query.

If you call a complicated SQL query which spans multiple tables the columns specified by the SELECT will be attributes of the model, whether or not they are columns of the corresponding table

For Models you can use column_names. For more info on the variations see other SA answer: How do you discover model attributes in Rails

Community
  • 1
  • 1
Stan Brajewski
  • 452
  • 2
  • 5
0

How are you creating these "report screens"? Are they erb templates? Are you just calling .each on columns to print them all out?

If that's the case you could override the columns() method in your models to return an ordered array.

Mike Deck
  • 18,045
  • 16
  • 68
  • 92
  • 1
    Using standard rails controller/view. For now I've got a separate array of column names that specified the order I want the columns to appear in. But this isn't DRY since the SQL already has these column names in the correct order. There should be a better way. – Jeff Waltzer Oct 16 '08 at 18:53