Rails. How can I make .sum method faster? - ruby-on-rails

I need to calculate total value for column :total_value in Order model. I try to do:
Order.where("created_at > :day", {:day => 10.day.ago}).where(user_id: 3, state: 'collected').sum(:total_value)
It works for me. But... Is there any possibility to do it faster?
Should I add indexes for :total_value and :created_at columns. Does it make a sense?

I'm not really that well aware of Rails internal optimizations, but the thing that first comes into my mind is combining the where clauses into one instead of two. Now it has to first create a result set for the first where and then apply other where on that set (assuming that Rails doesn't optimize it already).
Creating index for total_value makes no sense since you aren't looking for anything based on that column.

Related

Retrieving x number of random objects

I'm just trying to load 5 random objects in a rails controller
Thing.all(:limit => 5, :order => "RANDOM()")
Is that the least expensive way to do it?
Short answer: no.
What you have asked the db to do is: go order the entire thing table in a random order... then grab me five of them. If your thing table has a lot of rows... that's a very expensive operation.
A better option (if the ids are auto-increment and thus likely concurrent) is to generate a set of random ids within the id-range for your thing table and go fetch these individual things by those ids.
This is the best way:
Thing.all.sample(5)

Can I combine these two #update_all lines into one line?

I wrote a migration that does the following:
Event.update_all 'tom_cancelled = false', 'tom_cancelled IS NULL'
Event.update_all 'jerry_cancelled = false', 'jerry_cancelled IS NULL'
Can (and if I can, how do) I combine these together to dry it up? Would I use a block?
You can't unless you want to use a loop in ActiveRecord, but it doesn't make sense because you'll end up with one UPDATE query for every record in the result set + 1 for the SELECT.
In the current way, you only run two queries, no matter how many records are in the result set.
There is definitely no need to abstract more the current code. Especially considering it runs within a migration.

Static lookup on Rails Model

I'm trying to figure out the best way to model a simple lookup in my rails app.
I have a model, Coupon, which can be of two "types", either Percent or Fixed Amount. I don't want to build a database table around the "type" lookup, so what I'd like to do is just have a coupon_type(integer) field on the Coupon table which can be either 1 (for Percent) or 2 (for Fixed).
What is the best way to handle this?
I found this: Ruby on Rails Static List Options for Drop Down Box but not sure how that would work when I want each value to have two fields, both ID and Description.
I'd like this to populate a select list as well.
Thank you for the feedback!
If this is really unlikely to change, or if it does change it will be an event significant enough to require redeployment, the easiest approach is to have a constant that defines the conditions:
COUPON_TYPES = {
:percent => 1,
:fixed => 2
}
COUPON_TYPES_FOR_SELECT = COUPON_TYPES.to_a
The first constant defines a forward mapping, the second in a format suitable for options_for_select.
It's important to note that this sort of thing would take, at most, a millisecond to read from a database. Unless you're rendering hundreds of forms per second it would hardly impact performance.
Don't worry about optimizing things that aren't perceptible problems.

Rails query syntax

I'm kinda new on the Rails boat, I would like to know the difference between two types of syntax for queries
The first one I tried is:
User.limit(8).order('created_at DESC').group('created_at').count
The second, which seems to be far more efficient and powerful:
User.count(:order =>'DATE(created_at) DESC', :group =>["DATE(created_at)"], :limit => 8)
But I don't really understand the use case for both.
I'm sure this is something obvious anyway...
Thanks!
The first one is rails 3 syntax. And each method used there, i.e, limit, order, group are ActiveRecord:: Relation method. There are various advantages in using the 1st method. ActiveRecord::Relation is one of the core features of rails 3 apart from asset pipeline etc.
Please read this,
http://asciicasts.com/episodes/239-activerecord-relation-walkthrough
Well the second syntax is the deprecated, old-school, syntax. Also known as Hash-Options-Overload. The first, chaining, syntax is the way forward.
The second one is the powerful and efficient. Because first one will take all the rows (and all the columns) and then it will count. But the second one will perform only counting the rows.
Second one will use the following query.
select count(*) from tablename;

Rails 3 Migration: Autoincrement on (Non-Primary-Key) Column?

I'm looking for a way to create a column that autoincrements the way the automatic :id column does. I could probably handle this somehow in the model, but that seems kludgey. I haven't found anything in stock Rails 3 that handles this; are there gems available that might handle this? I'm surprised it's not already an option, since Rails handles this behavior for primary key columns.
Normally auto-incrementing columns are implemented using database sequences. The advantage of using a sequence over calculating the next increment, is that getting the next value from a sequence is atomic. So if you have multiple processes creating new elements, the sequence will make sure your numbers are really unique.
Sequences can be used in postgresql, oracle, mysql, ...
How to implement this, if you are using postgres for instance:
select the next value from the sequence:
Integer(Operator.connection.select_value("SELECT nextval('#{sequence_name}')"))
create a sequence:
Operator.connection.execute("CREATE sequence #{sequence_name}")
set the start-value of a sequence :
Operator.connection.execute("SELECT setval('#{sequence_name}', #{new_start_serial})")
Hope this helps.
If you really think you need this you could create a before_create filter in the model to check the last record attribute value and add 1 to it. Feels hacking though.

Resources