How to correctly use COUNT() when multiple joins are used? - sql

I have the following schema:
Multiple webinar entities can have multiple categories hence the webinarcategorymapping table.
What I need to achieve is find the most popular webinars (by likes number) of a specific category.
For doing this, I've written the query below:
select
webinar.id, webinar.name as "webinar", webinar.publishat,
string_agg(category.name, ',' order by category.name) as categories,
count("like".likeableid) as "likes_count"
from
webinar
join "like" on webinar.id = "like".likeableid and "like".likeabletype = 'webinar'
join webinarcategorymapping on webinarcategorymapping.webinarid = webinar.id
join category on category.id = webinarcategorymapping.categoryid
group by "like".likeableid, webinar.id
having
string_agg(category.name, ',' order by category.name) ilike '%CategoryName%'
and count("like".likeableid) > 0
order by count("like".likeableid) desc;
Due to the many-to-many relationship between category and webinar I've decided to join all categories for every webinar into a comma-separated value by using string_agg. This way I'll be able to perform the search by category by using ilike %search_term%.
In the like table the likeabletype must be equal to webinar and the likeableid filed is the id of an entity on which the like is made. So, in my case, when querying the like table I need to use likeabletype='webinar' and likeableid = webinar.id conditions.
The problem is that is gives me incorrect likes_count results (I guess it's due to multiple joins that duplicate many rows).
However using count(distinct "like".likeableid) doesn't help as it just gives me 1 for every row.
What should I change in my query in order to get correct result from count() of likes?

What I need to achieve is find the most popular webinars (by likes number) of a specific category.
You can aggregate the likes in a subquery and just filter on the categories:
select w.id, w.name as "webinar", w.publishat, num_likes
from webinar w join
(select l.likableid, count(*) as num_likes
from "like" l
where l.likeabletype = 'webinar'
group by l.likeableid
) l
on w.id = l.likeableid join
webinarcategorymapping wcm
on wcm.webinarid = w.id join
category c
on c.id = wcm.categoryid
where c.name = ?
order by num_likes desc;

Related

Faceted search count in SQL

I'm trying to implement faceted search count in SQL. For simplicity, I'll take the data that already exists on https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_all. A product has a category and a category belongs to many products, so it's a one-to-many relationship. I'm interested in filtering products by category, so if there are multiple categories selected, the query will get products whose category Id can be found in the list of Id's that the user filtered by (So it's an OR operation between categories). But this is not the challenge that I'm currently facing.
The query below tries to answer the question: For every category that exists, how many products would I get if that category was among the selected categories?
SELECT
cat.CategoryId,
p.Count
FROM Categories AS cat
LEFT JOIN (SELECT
COUNT(DISTINCT ProductId) AS Count
FROM Products AS p
WHERE p.CategoryId IN #CategoryIds
OR p.CategoryId = cat.CategoryId) AS p
The #CategoryIds is a parameter that is going to be handled by an ORM. For a more concrete scenario, you can just replace it with the list (1, 2) (so you can consider the case in which the user wants to filter all products that have the category 1 or 2).
The issue is that the word "cat" (on the last line) is not recognised so the query just throws an error.
Is there a way to make the second table recognise the first table's alias "cat" that I want to LEFT JOIN with? Or is there a better solution to this problem that I didn't take into consideration?
LEFT JOIN requires predicate. Some DBMS, like MS SQL Server, supports CROSS APPLY. This query should be equivalent to following one, ready to run on every SQL Database known to me:
SELECT
cat.CategoryId,
COUNT(ProductId)
FROM Categories AS cat
LEFT JOIN Products P ON p.CategoryId=cat.CategoryId OR p.CategoryId IN [list]
GROUP BY cat.CategoryId
Or, if you are using SQL Server:
SELECT
cat.CategoryId,
p.Count
FROM Categories AS cat
CROSS APPLY (SELECT COUNT(DISTINCT ProductId) AS Count
FROM Products AS p
WHERE p.CategoryId IN #CategoryIds
OR p.CategoryId = cat.CategoryId) AS p

How to perform count in access across three tables?

I am having a three tables, for a sake of simplicity let's say
Category (1...many) Topic (1...many) Post
What I am trying to achieve is to get a CategoryID and a total number of topics in this category as well as the total number of posts.
The best result what I made was using the following query:
SELECT category.ID, COUNT(topic.id) AS topiccount, COUNT(post.id) AS postcount
FROM ((category)
LEFT JOIN topic ON topic.categoryid = category.id)
LEFT JOIN post ON post.topicid = topic.id
GROUP BY category.id
Unfortunately, even if I have just a 6 topics in the table associated with a category I am getting '7' as a result.
I did some research on it and it seems that I have to use a DISTINCT keyword inside a COUNT however access does not support it and I could not find appropriate way to do it in the subqueries.
Thank you for any help!
You get one more record, because you are not counting the topics, you are actually counting the topic-post joined records. Use the following :
SELECT category.id, count(topic.id), Nz(Sum(numofposts),0)
FROM (category LEFT JOIN (
SELECT topic.id, count(post.id) as numofposts, topic.categoryId
FROM topic LEFT JOIN post on topic.id = post.topicId
GROUP BY topic.id, topic.categoryId
) as TP ON category.id=TP.categoryid)
GROUP BY category.id
The Nz is there to ensure that in empty topics you don't get Null sums

TSQL INNER JOIN

I'm building an IT support ticketing portal for a multi-site company.
I cant get my head around JOIN, INNER JOIN, ON , etc.
I have 3 tables:
Firstly Support_Ticket containing Site_ID, which I already have from a previous query.
So I have the Site_ID, and need to get the name (string) of the engineer that is responsible for support on that site. Lets say Hull is Site_ID:1.
The other 2 tables :
Site_Details: containing (among others) Site_ID and Site_Default_Engineer_ID.
Engineers: containing (among others) [Engineer_ID] and Engineer_Display_Name.
What I want to achieve is (pseudo-code!):
return [Engineers].[Engineer_Display_Name]
where
[Engineers].[Engineer_ID] = [Site_Details].[Site_Default_Engineer_ID]
(but first) return [Site_Details].[Site_Default_Engineer_ID] where
[Site_Details].[Site_ID] = [Support_Ticket].[Site_ID]
if that makes sense?!
This query should work:
SELECT support_ticket.something, engineers.engineer_display_name
FROM support_ticket
JOIN site_details ON ( site_details.site_id = support_ticket.site_id )
JOIN engineers ON ( engineers.engineer_id = site_details.site_default_engineer_id )
It will present all tickets and their default engineer. Add a WHERE-clause to filter the ticket(s) you want to display.
BTW: There is no difference between JOIN and INNER JOIN.

distinct group by join problem

Here's what I want to achieve:
I have a number of categories, each one with products in it.
I want to produce a report that shows various information about those products for each category. So I have a query that looks something like:
select
category,
count(products),
sum(product_price),
from product
group by category
So far so good.
But now I also want to get some category-specific information from a table that has information by category. So effectively I want to say:
join category_info on category
except that that will create a join for each row of each group, rather than just one join for each group.
What I really want to be able to say to sql is 'for each group, take the distinct category value, of which there's guaranteed to only be one since I'm grouping on it, and then use that to join to the category info table'
How can I accomplish this in SQL? By the way, I'm using Oracle 10g..
Many thanks!
select a.category, a.Count, a.SumPrice
ci.OtherColumn
from (
select p.category,
count(p.products) as Count,
sum(p.product_price) as SumPrice,
from product p
group by category
) a
inner join category_info ci on a.category = ci.category

Fetch last item in a category that fits specific criteria

Let's assume I have a database with two tables: categories and articles. Every article belongs to a category.
Now, let's assume I want to fetch the latest article of each category that fits a specific criteria (read: the article does). If it weren't for that extra criteria, I could just add a column called last_article_id or something similar to the categories table - even though that wouldn't be properly normalized.
How can I do this though? I assume there's something using GROUP BY and HAVING?
Try with:
SELECT *
FROM categories AS c
LEFT JOIN (SELECT * FROM articles ORDER BY id DESC) AS a
ON c.id = a.id_category
AND /criterias about joining/
WHERE /more criterias/
GROUP BY c.id
If you provide us with the Tables schemas, we could be a little more specific, but you could try something like (12.2.9.6. EXISTS and NOT EXISTS, SELECT Syntax for LIMIT)
SELECT *
FROM articles a
WHERE EXISTS (
SELECT 1
FROM articles
where category_id = a.category_id
AND <YourCriteria Here>
ORDER BY <Order Required : ID DESC, LastDate DESC or something?
LIMIT 1
)
Assuming the id's in the articles table represent always increasing numbers, this should work. Using the id is not semantically correct IMHO, you should actually use a time/date tamp field if one is available.
SELECT * FROM articles WHERE article_id IN
(
SELECT
MAX(article_id)
FROM
articles
WHERE [your filters here]
GROUP BY
category_id
)