Select all columns by a unique column value in Rails 3 - ruby-on-rails

In Rails 3, how do i select rows based on unique column values, i need to get all the columns for eg:
SELECT COUNT(DISTINCT date) FROM records
This only returns date column, but i want all the columns (name, date , age , created_at) columns not just the date.
Thanks for your help

The issue here is that, by definition, there may be multiple records with the same date. It requires logic in the user space to determine which of the multiple records with the unique date to use. Here's some code to get those rows:
Record.select("distinct date").each do |record|
records = Record.find_by_date record.date
puts records.count # do something with the records
end
If what you're really after is uniqueness among multiple columns, list all the relevant columns in the distinct query:
Record.select("distinct date, name, age, created_at").each do |record|
puts record.date
puts record.name
puts record.age
puts record.created_at
# ``record'' still represents multiple possible records
end
The fact that you are using distinct means that each "row" returned actually represents n rows, so the DB doesn't know which of the n rows to pull the remaining columns from. That's why it only returns the columns used in distinct. It can do no other...

I think this will help you
Model.find(:all, :select => 'DISTINCT name, date, age, created_at')
Please use it and let me know.

Model.group(:column)
For your case:
Record.group(:date)
This will return all your columns with no "date" repetitions.

For rails 3.2 and higher, Model.select('DISTINCT name, date, age, created_at')

Related

Rails & Active Record: Get the average value of a column after grouping

I have a table user_keywords which :belongs_to :keywords. user_keywords has a column keyword_id, user_id, and relevance_score (float).
keyword table has a column 'name'.
I want to group all the user_keywords by their keyword_id.
I want to take the average of each of those groups' relevance_score
I want to sort groups by the highest relevance score
I want to return the name of the keyword from the groups, sorted by highest relevance score.
What is the most efficient way to query this?
try this:
Keyword.joins(:user_keywords)
.select('keywords.name, avg(user_keywords.relevance_score) as score')
.group('keywords.name')
.order('score DESC')
.map(&:name)

Rails query: Group by one column and Order by sum of another column

I have a model PurchaseHistory. It has a column user_id and another column is amount. When any user purchased anything it stores the user_id and amount of purchase.
user_id amount
1 10
2 20
1 20
Now I want to make a query that will group by user_id and order by the total sum of amount of that user.
So, in this case the query should return something like this
[{1 => 30}, {2 => 20}]
This should work
(If it's PostgreSQL DB, use coalesce(amount, 0) in case there are any nils, otherwise use your corresponding db methods to cast nil to 0)
Hash[
PurchaseHistory.select("user_id, SUM(coalesce(amount, 0)) as amount_sum").group(:user_id).order("amount_sum DESC").map do |row|
[row.user_id, row.amount_sum]
end
]

Activerecord select columns from a triple join query

I have 3 models Passenger, Taxi, Order i want to get a certain column of these tables. I used this to join them successfully.
Taxi.joins(:orders => :passenger).where(:taxi =>{:taxi_id =>2 } )
I want to select some of the columns of passenger table and some of the column of order,
but i do not know how can i do that?
The other problem is that, the result of this query is only columns of Taxi and not the column of taxi+order+passenger
I think i have a problem with my query
Taxi
.joins(orders: :passenger)
.where(taxi: {taxi_id: 2 })
.pluck('passengers.column_name, orders.column_name')
Taxi.joins(orders: :passenger).where(taxi:{taxi_id: 2}).includes(orders: :passenger).map do |taxi|
#this will not query again as the includes directive above already fetched
taxi.orders.map {|p| puts p}
end

Sequel -- How To Construct This Query?

I have a users table, which has a one-to-many relationship with a user_purchases table via the foreign key user_id. That is, each user can make many purchases (or may have none, in which case he will have no entries in the user_purchases table).
user_purchases has only one other field that is of interest here, which is purchase_date.
I am trying to write a Sequel ORM statement that will return a dataset with the following columns:
user_id
date of the users SECOND purchase, if it exists
So users who have not made at least 2 purchases will not appear in this dataset. What is the best way to write this Sequel statement?
Please note I am looking for a dataset with ALL users returned who have >= 2 purchases
Thanks!
EDIT FOR CLARITY
Here is a similar statement I wrote to get users and their first purchase date (as opposed to 2nd purchase date, which I am asking for help with in the current post):
DB[:users].join(:user_purchases, :user_id => :id)
.select{[:user_id, min(:purchase_date)]}
.group(:user_id)
You don't seem to be worried about the dates, just the counts so
DB[:user_purchases].group_and_count(:user_id).having(:count > 1).all
will return a list of user_ids and counts where the count (of purchases) is >= 2. Something like
[{:count=>2, :user_id=>1}, {:count=>7, :user_id=>2}, {:count=>2, :user_id=>3}, ...]
If you want to get the users with that, the easiest way with Sequel is probably to extract just the list of user_ids and feed that back into another query:
DB[:users].where(:id => DB[:user_purchases].group_and_count(:user_id).
having(:count > 1).all.map{|row| row[:user_id]}).all
Edit:
I felt like there should be a more succinct way and then I saw this answer (from Sequel author Jeremy Evans) to another question using select_group and select_more : https://stackoverflow.com/a/10886982/131226
This should do it without the subselect:
DB[:users].
left_join(:user_purchases, :user_id=>:id).
select_group(:id).
select_more{count(:purchase_date).as(:purchase_count)}.
having(:purchase_count > 1)
It generates this SQL
SELECT `id`, count(`purchase_date`) AS 'purchase_count'
FROM `users` LEFT JOIN `user_purchases`
ON (`user_purchases`.`user_id` = `users`.`id`)
GROUP BY `id` HAVING (`purchase_count` > 1)"
Generally, this could be the SQL query that you need:
SELECT u.id, up1.purchase_date FROM users u
LEFT JOIN user_purchases up1 ON u.id = up1.user_id
LEFT JOIN user_purchases up2 ON u.id = up2.user_id AND up2.purchase_date < up1.purchase_date
GROUP BY u.id, up1.purchase_date
HAVING COUNT(up2.purchase_date) = 1;
Try converting that to sequel, if you don't get any better answers.
The date of the user's second purchase would be the second row retrieved if you do an order_by(:purchase_date) as part of your query.
To access that, do a limit(2) to constrain the query to two results then take the [-1] (or last) one. So, if you're not using models and are working with datasets only, and know the user_id you're interested in, your (untested) query would be:
DB[:user_purchases].where(:user_id => user_id).order_by(:user_purchases__purchase_date).limit(2)[-1]
Here's some output from Sequel's console:
DB[:user_purchases].where(:user_id => 1).order_by(:purchase_date).limit(2).sql
=> "SELECT * FROM user_purchases WHERE (user_id = 1) ORDER BY purchase_date LIMIT 2"
Add the appropriate select clause:
.select(:user_id, :purchase_date)
and you should be done:
DB[:user_purchases].select(:user_id, :purchase_date).where(:user_id => 1).order_by(:purchase_date).limit(2).sql
=> "SELECT user_id, purchase_date FROM user_purchases WHERE (user_id = 1) ORDER BY purchase_date LIMIT 2"

Is it possible to do: user = User.find(234234) and somehow ignore some columns in the select query?

I have some columns that contain a large amount of data (text, etc), and sometimes I am just displaying a summary list of the rows and I don't want to fetch all that data.
Is there an option to exclude certain columns?
I don't believe you can exclude certain columns, but you can have ActiveRecord return only specific columns using the :select parameter, e.g.
#articles = Article.find( :all, :select => ‘created_at, title, summary’ )
you can find by sql query so you can fetch your required data and its easy
Post.find_by_sql("SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date)

Resources