Rails ActiveRecord only return distinct records based on a column - sql

Suppose I have an ActiveRecord model called Checkin and I only want to return Checkin records distinct by the user_id. Is there way to do this that returns an AR relation? I need to apply multiple scopes to it so I would prefer to avoid find_by_sql.
Example:
Let's say we have the following records
id: 1
user_id : 5
location_id: 12
id: 2
user_id: 25
location_id: 12
id: 3
user_id: 5
location_id: 12
I want to be able to say something like:
Checkin.distinct_by(:user_id) and have that return only rows 1 and 2 (because user_id=5 should be distinct). I would like this to be an ActiveRecord:Relation ideally so that I can apply other conditions onto this.

I think you should make your question more clear, can you provide some example? I don't know this is what you are looking for: you can try to use "select" or "collect" function after your where statement.

Related

PostgreSQL, add prefix to record field in SQL

the problem is:
is there a query in postgreSQL that will add some prefix to every record specific field? For example let's assume we have table users:
**id** **name**
1 adam
2 ewa
and i want to get records and add to every id some specific prefix - let's say 'foo_', desirable result:
id: 'foo_1', name: adam
id: 'foo_2', name: ewa
I know it can be done with Python after retrieving records, but i wonder if possible with query.
Thanks in advance!!
select 'foo_'||id::text,
name
from the_table;
You can just use a concatenation operator String Functions and Operators
SELECT 'foo_' || id::text FROM table

Return a grouped list with occurrences using Rails and PostgreSQL

I have a list of Tags in my rails application which are linked to Posts using Taggings. In some views, I'd like to show a list of the 5 most commonly used tags, together with the times they have been tagged. To create a complete example, assume a table with 3 posts:
POSTS
ID | title
1 | Lorem
2 | Ipsum
3 | Dolor
And a table with 2 Tags
TAGS
ID | name
1 | Tag1
2 | Tag2
Now, if Post 1 is tagged with Tag1 and post 2 is tagged with tags 1 and 2, our taggings table looks like this:
TAGGINGS
tag_id | post_id
1 | 1
1 | 2
2 | 2
Then the question is how to fetch the required information (preferably without going back to the DB multiple times) to display the following result:
Commonly used tags:
tag1 (2 times)
tag2 (1 time)
I've managed to do this using MySQL by including the tag, grouping by tag_id and ordering by the count:
Ruby:
taggings = Tagging.select("*, count(tag_id) AS count").includes(:tag).group(:tag_id).order('count(*) DESC').limit(5)
taggings.each { |t| puts "#{t.tag.name} (#{t.count})" }
However, I'm moving the app to Heroku, and therefore I'm required to move to PostgreSQL 9.1. Unfortunately the strictness of Postgres breaks that query because it requires all fields to be specified in the group by clause. I've tried going that way, but it has resulted in the fact that I can't use t.count anymore to get the count of the rows.
So, to finally get to the question:
What is the best way to query this kind of information from postgres (v9.1) and display it to the user?
Your problem:
Unfortunately the strictness of Postgres breaks that query because it requires all fields to be specified in the group by clause.
Now, that has changed somewhat with PostgreSQL 9.1 (quoting release notes of 9.1):
Allow non-GROUP BY columns in the query target list when the primary
key is specified in the GROUP BY clause (Peter Eisentraut)
What's more, the basic query you describe would not even run into this:
Show a list of the 5 most commonly used tags, together
with the times they have been tagged.
SELECT tag_id, count(*) AS times
FROM taggings
GROUP BY tag_id
ORDER BY times DESC
LIMIT 5;
Works in any case.

Postgresql (Rails 3) merge rows on column (same table)

First, I've been using mysql for forever and am now upgrading to postgresql. The sql syntax is much stricter and some behavior different, thus my question.
I've been searching around for how to merge rows in a postgresql query on a table such as
id | name | amount
0 | foo | 12
1 | bar | 10
2 | bar | 13
3 | foo | 20
and get
name | amount
foo | 32
bar | 23
The closest I've found is Merge duplicate records into 1 records with the same table and table fields
sql returning duplicates of 'name':
scope :tallied, lambda { group(:name, :amount).select("charges.name AS name,
SUM(charges.amount) AS amount,
COUNT(*) AS tally").order("name, amount desc") }
What I need is
scope :tallied, lambda { group(:name, :amount).select("DISTINCT ON(charges.name) charges.name AS name,
SUM(charges.amount) AS amount,
COUNT(*) AS tally").order("name, amount desc") }
except, rather than returning the first row of a given name, should return mash of all rows with a given name (amount added)
In mysql, appending .group(:name) (not needing the initial group) to the select would work as expected.
This seems like an everyday sort of task which should be easy. What would be a simple way of doing this? Please point me on the right path.
P.S. I'm trying to learn here (so are others), don't just throw sql in my face, please explain it.
I've no idea what RoR is doing in the background, but I'm guessing that group(:name, :amount) will run a query that groups by name, amount. The one you're looking for is group by name:
select name, sum(amount) as amount, count(*) as tally
from charges
group by name
If you append amount to the group by clause, the query will do just that -- i.e. count(*) would return the number of times each amount appears per name, and the sum() would return that number times that amount.

How can I select only rows with multiple hits for a specific column?

I am not sure how to phrase this question so I'll give an example:
Suppose there is a table called tagged that has two columns: tagger and taggee. What would the SQL query look like to return the taggee(s) that are in multiple rows? That is to say, they have been tagged 2 or more times by any tagger.
I would like a 'generic' SQL query and not something that only works on a specific DBMS.
EDIT: Added "tagged 2 or more times by any tagger."
HAVING can operate on the result of aggregate functions. So if you have data like this:
Row tagger | taggee
--------+----------
1. Joe | Cat
2. Fred | Cat
3. Denise | Dog
4. Joe | Horse
5. Denise | Horse
It sounds like you want Cat, Horse.
To get the taggee's that are in multiple rows, you would execute:
SELECT taggee, count(*) FROM tagged GROUP BY taggee HAVING count(*) > 1
That being said, when you say "select only rows with multiple hits for a specific column", which row do you want? Do you want row 1 for Cat, or row 2?
select distinct t1.taggee from tagged t1 inner join tagged t2
on t1.taggee = t2.taggee and t1.tagger != t2.tagger;
Will give you all the taggees who have been tagged by more than one tagger

Count and Select Object in ActiveRecord with 1 query

We have objects that we want to represent in stacks (think of stacking items in an MMO). There will be duplicate rows.
Let's say our owned_objects table looks like this.
user_id | object_id
1 | 27
1 | 27
3 | 46
3 | 46
5 | 59
I want the query to do
SELECT
user_id,
object_id,
count(*) AS count
FROM owned_objects
GROUP BY
user_id,
object_id;
And return either the 3 distinct OwnedObjects (or even just getting the distinct Objects would work too) and a count associated with it.
I know this is possible with SQLAlchemy, but can you do it with ActiveRecord?
How about …
#objects = user.objects.all(:select => "count(*) as count, objects.*", :group => :object_id)
… or similar?
You can then retrieve the counts by a dynamically created attribute on each object:
#object.first.count # the "stack depth" of the first object.
This assumes either a has_and_belongs_to_many :objects or a has_many :objects, :through => :owned_objects on user.
Found a solution, but not sure if it's the cleanest (hope it isn't).
Basically I created a SQL view that does that query and created a model for it. There's a plugin for rails that recognizes views on migrations.