Query column of arrays with array - ruby-on-rails

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)

Related

Return unique user doesn't work with group_by

I'm using the ajax datatable gem to display the client name and total points in a store. the store-clients table looks like this.
store_id
client_id
total_points
But as it will be a report, I don't want to show the same client twice, even to different stores. I am trying the following:
StoreClient.joins(:client).group(:client_id)
But I get this error: column "store_clients.id" must appear in the GROUP BY clause or be used in an aggregate function
If I add the store_clients id the result is not as expected.
The expected would be:
client_id | store_id | total_points
1 1 10
1 2 20
2 1 5
-----------------------------------
client_id | store_id | total_points
1 1 10
2 1 5
Assume initial result is an array store in instance variable #result you can get a unique result like this:
#result.uniq {|o| o.client_id }

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.

Limit PER user in rails query

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.

ActiveRecord query that groups by ID and sums rows

My Postgresql DB has this structure:
TABLE Orders
id (string)
userId (string)
prodId (string)
value (integer)
This is an example of data:
id userId prodId value
1 a#a.aaa prod1 5
2 b#b.bbb prod1 -1
3 a#a.aaa prod1 -4
4 a#a.aaa prod2 9
I want to do a query from ActiveRecord that sums all the values for a specific userId, so the query for (a#a.aaa) would return a LIST like this:
prod1 1
prod2 9
My first approach is this one, but it doesn't work:
orderList = Orders.select("SUM(orders.amount) AS num_prods").where((:userId => HERE_USER_ID).group(:prodId)
EDIT: rephrased thanks to feedback
Order.where(userId: id).group(:prodId).sum(:value) # replace `:id` with your value
This should give you a hash, like so
{1=>10, 2=>20, 5=>20}
the keys 1,2,5 represent the product id, and the values 10,20,20 represent the sum values.

Rails ActiveRecord return records where id exists in related table

I have a Client model and a Product model where a Client has many Products and a Product belongs to a CLient.
I need to find a query that only returns Clients if they have a record in the Product table
clients table
id | name
--------------
1 | Company A
2 | Company B
3 | Company C
products table
id | name | client_id
---------------------------
1 | Product A | 1
2 | Product B | 1
3 | Product C | 3
4 | Product D | 3
5 | Product E | 1
I only need Clients 1 3
For example something like
#clients = Client.where("client exists in products") #something to this effect
Simplest but not the fastest:
Client.where(:id => Product.select(:client_id).map(&:client_id))
SQL subquery (more faster):
Client.where("EXISTS(SELECT 1 from products where clients.id = products.client_id)")
Here's another solution. It's a subquery like Valery's second solution, but without writing out the sql:
Client.where(Product.where(client_id: Client.arel_table[:id]).exists)
Here is the solution which uses Where Exists gem (disclosure: I'm its author):
Client.where_exists(:products)
Another gem that exists to do that: activerecord_where_assoc (I'm the author)
With it:
Client.where_assoc_exists(:products)
If you had to also specify some of the products, when you could do it like this:
Client.where_assoc_exists(:products, id: my_products.map(&:id))
Doing it without a gem makes it easy to do mistakes.
Read more in the documentation. Here is an introduction and examples.
Also not the fastest but is concise:
Client.where(:id => Product.pluck(:client_id))

Resources