Query for Joining Multiple Associations - sql

I managed to make query with joined table. I wonder how could I add another join table (e.g. publication_comments), count how many comments publication have, sum publication_comments_count with reviews_count and order in DESC order.
I have read rails guide and many example on stackoverflow, however still need somebody to assist on syntax and how exactly should I gather all pieces in one place. Thanks.
#publication = Publication.all(
joins: :reviews,
select: '"publications".*, count("reviews".id) as reviews_count',
group: '"publications".id',
order: "reviews_count DESC")

ActiveRecord allows you to just supply a string to the joins method. So you can just write raw SQL join conditions:
Publication.select("..., COUNT(publication_comments.id) as pub_count")
.join('JOIN reviews on (..) JOIN publication_comments')
.group('publications.id'

Related

How to write a query that lists the name of someting that appears in more than one category

I'm new so please excuse me if this is a dumb question but i can't figure it out.
Question: Write a query that lists the name of Products that appear in more than one Recipe.
These are the tables
The simplest solution to this question is to use a GROUP BY expression with a HAVING clause to limit the results to groups that have a count of more than 1
SELECT Nome_produto
FROM Receita
LEFT JOIN Ingrediente ON Receita.Codigo_receita = Ingrediente.Codigo_receita
LEFT JOIN Produto ON Ingrediente.Codigo_produto = Produto.Codigo_produto
GROUP BY Nome_produto
HAVING COUNT(Receita.Codigo_receita) > 1
Nome_produto
Ovos
The key to solving an issue like this is to first build the relationship between the entities that the requirement refers to, in this case I'm selecting data FROM the recipe table first, as this is the subject that the principal predicate must be applied to:
...that appear in more than one Recipe.
Once you have a denormalized or flat table of results, we can apply the grouping with a HAVING clause. HAVING is similar to WHERE except that it is evaluated after the grouping and allows us to use the results from aggregate expressions in conditional statements.
WHERE is applied to individual rows and will exclude rows before the grouping sets are realized and therefore before aggregates are evalutated.
Read more about SQL HAVING Clause here # W3 Schools.

Having troubles with a conditional count in SQL

I'm working on an SQL project (involving a library database) and I'm having a hard time figuring out how to make a conditional count.
So, I have 4 tables: Imprumuturi, Cititori, Autori, Carti. I need to list the 'Cititori' that have more than one borrowed 'Carti' at the current time.
I tried to use
SELECT cititori.nume_cititor,COUNT(imprumuturi.pk_cititor)
AS numar_imprumuturi FROM cititori, imprumuturi
WHERE imprumuturi.data_return IS NULL GROUP BY cititori.nume_cititor
HAVING COUNT(imprumuturi.pk_cititor)>1
ORDER BY cititori.nume_cititor;
And while it lists all the 'Cititori", it doesn't count the number of active borrowed 'Carti' as it should.
Can I get a hint or some help on how to make it work?
These are the fields of my database
Seems you missed the relation between the tables:
SELECT cititori.nume_cititor,COUNT(imprumuturi.pk_cititor)
AS numar_imprumuturi
FROM cititori
INNER JOIN imprumuturi ON imprumuturi.pk_cititori = cititori.pk_cititori
WHERE imprumuturi.data_return IS NULL
GROUP BY cititori.nume_cititor
HAVING COUNT(imprumuturi.pk_cititor)>1
ORDER BY cititori.nume_cititor;
As suggested, you should not use the old implicit join syntax based on comma-separated table names and where condition, but use explicit join syntax.

Issue counting joined rows from two tables

My schema, query, and problematic results can be seen here:
http://sqlfiddle.com/#!17/55bc3/5/0
I've created a schema for storing posts, comments, and favourites. ( I've simplified my example for the sake of demonstration ). I'm trying to write a query to aggregate the like/favourite counts for each post, for display on a 'front page'.
To model the relationships between users/posts/favourites I've used multiple intersection tables. In the query I'm using two LEFT JOINs, and then COUNTing distinct columns in the results. I've encountered an issue where the COUNT I'm storing as comment_count overrides favourite_count when it returns anything above 0, causing it to return duplicate values for both columns.
I think I understand the mechanism behind this, being that the GROUPing of the results is causing the resulting rows to get squashed together to yield an incorrect result. I was wondering if anyone could let me know some of the theory behind what this is called, and how you would correctly write queries to handle this scenario.
As they are unrelated tables, you can count individually and then join.
SELECT p.id
,coalesce(c.comment_count,0) as comment_count
,coalesce(f.favorite_count,0) as favorite_count
FROM post p
LEFT JOIN (select post_id,count(*) as comment_count
from comment group by post_id) c ON c.post_id=p.id
LEFT JOIN (select post_id,count(*) as favorite_count
from favourite group by post_id) f ON f.post_id=p.id

Rails ActiveRecord where or clause

I'm looking to write an ActiveRecord query and this is what I have below. Unfortunately you can't use OR like this. What's the best way to execute? category_ids is an array of integers.
.where(:"categories.id" => category_ids).or.where(:"category_relationships.category_id" => category_ids)
One way is to revert to raw sql...
YourModel.where("categories.id IN ? OR category_relationships.category_id IN ?", category_ids, category_ids)
Keep the SQL out of it and use ARel, like this:
.where(Category.arel_table[:id].in(category_ids).
or(CategoryRelationship.arel_table[:category_id].in(category_ids))
Assuming you want to return Categories, you need to OUTER JOIN category_relationships and put a OR condition on the combined table.
Category.includes(:category_relationships).where("categories.id IN (?) OR category_relationships.category_id IN (?)",category_ids,category_ids )
This query is creating an outer join table by combining columns of categories and category_relationships. Unlike an inner join (e.g. Category.joins(:category_relationships)), outer join table would also have categories with no associated category_relationship. It would then apply the conditions in whereclause on the outer join table to return the matching records.
includes statement without conditions on the association usually makes two separate sql queries to retrieve the records and their association. However when used with conditions on the associated table, it would make a single query to create an outer join table and run conditions on the outer join table. This allows you to retrieve records with no association as well.
See this for a detailed explanation.
What you want to do is manually write the OR part of the query like this:
.where("category.id in (#{category_ids.join(',')}) OR category_relationships.category_id in (#{category_ids.join(',')})")

Rails Inner Join not working but the SQL looks right

So I have 2 tables that are joined by an ID. I'm in rails console and I type:
Programmer.all(:joins=>:assignment)
the sql that is generated is:
SELECT `programmers`.* FROM `programmers` INNER JOIN `assignments` ON `assignments`.`programmer_id` = `programmers`.`id`
The output that is generated is the same as Programmer.all. Why doesn't it include the assignments data?
I believe I majorly overanalyzed your question. If you just want to join any available assignments to programmers, you're looking for:
Programmer.all(:include => :assignment)
Rails is designed so that :joins is used to perform things like sorting and grabbing certain records but still keep the query result to a minimum size -- meaning, :joins never actually includes the results from the joined table in the result.
Now here's my previous answer that assumes you want to perform an INNER JOIN to get only the programmers with assignments, but you also want that data. In that case, you have two options:
#1 - Use :select
Programmer.all(:select => '*', :joins => :assignment)
That will change the SQL to:
SELECT * FROM `programmers` INNER JOIN `assignments` ON `assignments`.`programmer_id` = `programmers`.`id`
Upside: You get the query you want and all the data is somewhere, at least.
Downside: assignments is assigned directly to the Programmer object and not to the proper place at Programmer.assignment.
#2 - Use a combination of :joins and :includes
Programmer.all(:joins => :assignment, :include => :assignment)
Which produces the SQL:
SELECT `programmers`.* FROM `programmers` INNER JOIN `assignments` ON `assignments`.`id` = `programmers`.`assignment_id`
SELECT `assignments`.* FROM `assignments` WHERE (`assignments`.`id` IN (?) )
Upside: All your data is in the right place now. You can refer to programmer.assignment without another query.
Downside: You are running that extra query in a lot of instances. I am fairly sure that Rails tries to optimize this when it needs to, though, and if not, it shouldn't cause you too much overhead.
Simply you can do like
Programmer.includes(:assignment)