Limit PER user in rails query - ruby-on-rails

So I have a standard users table structure, with a primary id key and what so not and the following persona table:
user_id | persona_id | time_inserted
2 1 x
2 2 x+1
2 3 x+2
1 1 x+3
5 8 x+6
5 9 x+1
What I'd like to do is retrieve the LAST inserted row and limit to ONE per user id. So, in that query, the result I want would be:
[2, 3] because the last inserted for 2 was persona_id 3 (x+2), [1, 1], and [5,8] because the last inserted for 5 was persona_id 8 (x+6)
This is my query:
to_return = Persona.select(to_get).where(to_condition)
This works, but retrieves them all. How can I restrict the query as asked? Thank you very much.

This should work:
to_return = Persona.select(to_get).where(to_condition).group('user_id').having('time_inserted = MAX(time_inserted)')
Update
You can't select a column if you don't put that in the group clause.
As you want to group by only user_id, one possible solution is, select the user_id s first with the maximum time_inserted like this:
users_ids_relation = Persona.select('user_id').group('user_id').having('time_inserted = MAX(time_inserted)')
Then, join it with the personas table based on the condition and then select the required columns:
users_ids_relation.joins('personas').where(to_condition).select(to_get)
It will give you the expected result.

Related

how can I get unique user_id from Post

How can I get unique values from column in the table? For example, I have this Products table:
user_id number
1 900
1 400
2 100
2 300
3 200
Here I used....
like so
Post.order(number: :desc).limit(3).each do |p| > I got here
user_id number
1 900
1 400
2 300
like so
Here I want "user_id" not to overlap. like this
f
user_id number
1 900
2 300
3 200
As deepak adviced, you might use distinct.
However, I guess you need to get maximum value from all records with the same user_id? Then you need to use sql to group values by user_id and selecting max from it. In Rails that might be like this. I can't tell you that it will work for sure because don't remember the output, believe it's array of hashes, it might throw the error that you're not using some other field:
Post.group(:user_id).max(:value)
it's SQL equalent:
SELECT user_id, MAX(value) FROM posts GROUP BY user_id

rails sort by specifc order

I have model named Group which has users_count field in it.
I have to order groups based upon dynamic preference of users_count like [3,4,2]
means show groups with 3 users_count first, than with 4 and than with 2
Right now, I am using 3 separate queries and than merge records like
groups = Group.where(users_count: 3)
+ Group.where(users_count: 4)
+ Group.where(users_count: 2)
It works but It don't make sense to use 3 separate queries.
How can I achieve this in single query?
Since 3, 4, 2 is not a sequential order there should be a custom condition to order them properly you can do it by using the CASE WHEN expression.
order_sql = Arel.sql(
'CASE WHEN users_count = 3 THEN 0 ' \
'WHEN users_count = 4 THEN 1 ' \
'ELSE 3 END'
)
Group.where(users_count: [2,3,4]).order(order_sql)
Which will give 0 when users_count = 3, 1 when users_count = 4, and 3 for other cases. With default ascending order you'll get the result you want.
You can do
groups = Group.where(users_count: [3,4,2])
This will return the same groups as your 3 queries in a single query

Many-to-many SELECT divided into multiple rows for some reason

I have two tables joined via third in a many-to-many relationship. To simplify:
Table A
ID-A (int)
Name (varchar)
Score (numeric)
Table B
ID-B (int)
Name (varchar)
Table AB
ID-AB (int)
A (foreign key ID-A)
B (foreign key ID-B)
What I want is to display the B-Name and a sum of the "Score" values of all the As belonging to the given B. However, the following code:
WITH "Data" AS(
SELECT "B."."Name" As "BName", "A"."Name", "Score"
FROM "AB"
LEFT OUTER JOIN "A" ON "AB"."A" = "A"."ID-A"
LEFT OUTER JOIN "B" ON "AB"."B" = "B"."ID-B")
SELECT "BName", SUM("Score") AS "Total"
FROM "Data"
GROUP BY "Name", "Score"
ORDER BY "Total" DESC
The results display several rows for every "BName" with the "score" divided into semingly random increments between these rows. For example, if the desired result for Johnny is 12 and for April it's 25, the query may shows something like:
Johnny | 7
Johnny | 3
Johnny | 2
April | 19
April | 5
April | 1
etc.
Even after trying to nest the query and doing another SELECT with SUM("Score"), the results are the same. I'm not sure what I'm doing wrong?
Remove Score from the GROUP BY clause:
SELECT BName, SUM(Score) AS Total
FROM Data
GROUP BY BName
ORDER BY Total DESC;
The purpose of your query is to summarize by name, so name alone should appear in the GROUP BY clause. By also including the score, you will get a record in the output for each unique name/score combination.
Okay, I figured out my problem. Indeed, I had to GROUP BY "Name" only, but Firebird I thought wasn't letting me do that. Turns out it was just a typo. Oops.

Order by nil value in column

I have table with column position, which in some cases, for some collection of records can be nil. I have default order options like
order('positions ASC')
id| name | position
1 5 null
2 6 null
3 7 null
If for some collection that I sort (example above), all values have null in position column, in which order I will get this collection from db?
I'm suggestion I will get collection in order of ids (1,2,3). Am I correct?
Addition #1: DB - Postgresql
According Postgres manual, if no sorting clause the records are returned according with physical position at the disk. It says nothing for sorted records with equal values on sort fields. But, it uses b-tree and, like clasic db managers, it must return on the order stored at the b-tree. You must expect that each of this change on db reorganization.
At the end, there are no warranty on the order of records with same values on sort fields.
Note: using Postgres you can make the NULL values at the first or the last (it is detailed at the referrer link).
At this related question, I'm agree with #macek.
You can do something like this.
Cats:
id| name | position
1 5 null
2 6 null
3 7 not_null
nil = Cat.order("id ASC").where(position: nil) = [1, 2]
not_nil = Cat.order("id ASC").where("position is not null") = [3]
not_nil + nil = [3, 1, 2]
This preserves order.

Query column of arrays with array

I have a database column with the datatype array. How can I query an array with an array in rails? For example, I have an array of software for an OS in my Product table and I want to query them like this:
selected_products = ['Apple', 'Linux']
Software.where("'#{selected_products}' = ANY (osses)")
edit:
example of Software table:
id | software | osses |
_______________________________________________________
1 product 1 {'Windows', 'Apple', 'Android'}
2 product 2 {'Android', 'Linux'}
3 product 3 {'Windows,', 'Android'}
4 product 4 {'Windows'}
In which case I'd like the query to return id's 1 and 2 (since id 1 has a match in Apple and id 2 has a match in Linux.)
Please look into this gem.
And then do a query like
selected_products = ['Apple', 'Linux']
Software.where.overlap(osses: selected_products)

Resources