Range of IDs via ActiveRecord - ruby-on-rails

I understand that I can use People.first(100) to retrieve the first 100 records, same goes for People.last(100).
What I don`t know, is how do I retrieve all objects in the range of 200-400, when the total number is lets say a 1000 records ?

What you need is limit and offset - read this for more info.
Example:
People.limit(200).offset(200)
The above code takes 200 records starting from 201st record - that means it would be records 201-400.

Are you searching on a specific field, your title suggests you're searching on id?
People.where('id BETWEEN ? AND ?', 200, 400)
or...
People.where(id: 200..400)
If you're not searching on a particular field, you would want to use Big_Bird's limit and offset methods.

Related

Is there a way with Rails/Postgres to get records in a random order BUT place certain values first?

I have an app that returns a bunch of records in a list in a random order with some pagination. For this, I save a seed value (so that refreshing the page will return the list in the same order again) and then use .order('random()').
However, say that out of 100 records, I have 10 records that have a preferred_level = 1 while all the other 90 records have preferred_level = 0.
Is there some way that I can place the preferred_level = 1 records first but still keep everything randomized?
For example, I have [A,1],[X,0],[Y,0],[Z,0],[B,1],[C,1],[W,0] and I hope I would get back something like [B,1],[A,1],[C,1],[Z,0],[X,0],[Y,0],[W,0].
Note that even the ones with preferred_level = 1 are randomized within themselves, just that they come before all the 0 records. In theory, I would hope whatever solution would place preferred_level = 2 before the 1 records if I were ever to add them.
------------
I had hoped it would be as intuitively simple as Model.all.order('random()').order('preferred_level DESC') but that doesn't seem to be the case. The second order doesn't seem to affect anything.
Any help would be appreciated!
This got the job done for me on a very similar problem.
select * from table order by preferred_level = 1 desc, random()
or I guess the Rails way
Model.order('preferred_level = 1 desc, random()')

apply ordering on limit query of active record

I need to find 10 records first and then apply ordering on that.
Model.all.limit(10).order('cast(code as integer)')
result of above code is - first it applies order on model and then limit query. So, I get same codes in my listing for given model. But I want to apply limit query first and then order fetched result.
When you call .all on model, it executes the query on DB and returns all records, to apply limit you have to write it before .all - Model.limit(10).all, but after that you can't use SQL function to operate data. So to get first 10 records and apply order to it, try this:
records = Model.limit(10).all.sort_by{|o| o.code.to_i}
or
records = Model.first(10).sort_by{|o| o.code.to_i}
Try this:
Model.limit(10).sort{|p, q| q.cost <=> p.cost}
All you need to do is to remove the .all method:
Model.limit(10).order('cast(code as integer)')
If you want to get 10 random records and then sort them without getting all records from the database then you could use
Model.limit(10).order("RANDOM()")
This uses the PostgreSQL rand() function to randomise the records.
Now you have an array of 10 random records and you can use .sort_by as described by BitOfUniverse.

ActiveRecord: Alternative to find_in_batches?

I have a query that loads thousands of objects and I want to tame it by using find_in_batches:
Car.includes(:member).where(:engine => "123").find_in_batches(batch_size: 500) ...
According to the docs, I can't have a custom sorting order: http://www.rubydoc.info/docs/rails/4.0.0/ActiveRecord/Batches:find_in_batches
However, I need a custom sort order of created_at DESC. Is there another method to run this query in chunks like it does in find_in_batches so that not so many objects live on the heap at once?
Hm I've been thinking about a solution for this (I'm the person who asked the question). It makes sense that find_in_batches doesn't allow you to have a custom order because lets say you sort by created_at DESC and specify a batch_size of 500. The first loop goes from 1-500, the second loop goes from 501-1000, etc. What if before the 2nd loop occurs, someone inserts a new record into the table? That would be put onto the top of the query results and your results would be shifted 1 to the left and your 2nd loop would have a repeat.
You could argue though that created_at ASC would be safe then, but it's not guaranteed if your app specifies a created_at value.
UPDATE:
I wrote a gem for this problem: https://github.com/EdmundMai/batched_query
Since using it, the average memory of my application has HALVED. I highly suggest anyone having similar issues to check it out! And contribute if you want!
The slower manual way to do this, is to do something like this:
count = Cars.includes(:member).where(:engine => "123").count
count = count/500
count += 1 if count%500 > 0
last_id = 0
while count > 0
ids = Car.includes(:member).where("engine = "123" and id > ?", last_id).order(created_at: :desc).limit(500).ids #which plucks just the ids`
cars = Cars.find(ids)
#cars.each or #cars.update_all
#do your updating
last_id = ids.last
count -= 1
end
Can you imagine how find_in_batches with sorting will works on 1M rows or more? It will sort all rows every batch.
So, I think will be better to decrease number of sort calls. For example for batch size equal to 500 you can load IDs only (include sorting) for N * 500 rows and after it just load batch of objects by these IDs. So, such way should decrease have queries with sorting to DB in N times.

Sybase compare columns with duplicate row ids

So far I have a query with a result set (in a temp table) with several columns but I am only concerned with four. One is a customer ID(varchar), one is Date (smalldatetime), one is Amount(money) and the last is Type(char). I have multiple rows with the same custmer ID and want to evaluate them based on Date, Amount and Type. For example:
Customer ID Date Amount Type
A 1-1-10 200 blue
A 1-1-10 400 green
A 1-2-10 400 green
B 1-11-10 100 blue
B 1-11-10 100 red
For all occurrences of A I want to compare them to identify only one, first by earliest date, then by greatest Amount, then if still tied by comparing Types. I would then return one row for each customer.
I would provide some of the query but I am at home now after spending two days trying to get a correct result. It looks something like this:
(query to populate #tempTable)
GROUP BY customer_id
HAVING date_cd =
(SELECT MIN(date_cd)
FROM order_table ot
WHERE ot.customerID = #tempTable.customerID
)
OR date_cd IS NULL
I assume the HAVING would result in only one row per customer_id. This did not end up being the case since there were some ties there.
I am not sure I can do the OR - there are some with NULL values here - and it did not account for the step to the next comparison if they were all the same anyway. I am not seeing a way to avoid doing some row processing of the temp table with some kind of IF or WHERE loop.
As I write I am thinking maybe I use #tempTable.date_cd in the HAVING clause instead of looking at the original table. but that should return the same dates?
Am I on the right track or is there something missing? Suggestions? More info??
try below query :-
select * from #tempTable
GROUP BY customer_id
HAVING isnull(date_cd,"1900/01/01") =min(isnull(date_cd,"1900/01/01"))

Given a query how to reduce the number of items

I'm trying to do the following in rails 3:
#groups_active = Group.active_groups(current_user)
active_groups is a scope. This query works fine. I then want to do the following:
if #groups_active.count > 9
#groups_active[0..10]
end
Meaning if there are more than 10 items in the #groups_active, take just the TOP 10, which thanks to the scope ordering are the most active.
Suggestions? Thanks
I'm not sure what your problem is. You can limit the number of results from a query with Model.your_scope.limit(10), and if it is a query that doesn't work with a SQL LIMIT then you can use Model.your_scope.first(10). That's an Array#first, which accepts a fixnum argument to be used as expected…
#groups_active = Group.active_groups(current_user).limit(10)
or you could add the .limit(10) part to your scope.
Edited
I would go with one limit(10) request and one count. It could be more effective then retrieving 20, 50, or more records and using only the first 10 of them.
But still, it requires testing and benchmarking.

Resources