Count/sum rows and group them by another column - sql

I am starting with SQL and I have a problem grouping 2 columns. I need the counting to be filtered by another column, but I obtain the count of all the rows as if they weren't grouped.
I have 4 different tables with countries, cities, matches and stadiums. I have to obtain names and codes of countries and cities as well as the number of referees that worked in each city and the total number of goals made in every city. I have done this, but I obtain the total number of referees that have worked in the tournament and the total number of goals made during the tournament:
SELECT ci.city_code,
ci.city_name,
co.country_code,
co.country_name,
COUNT(DISTINCT referee_code),
SUM(home_goals+visitor_goals)
FROM euro2021.tb_city AS ci
INNER JOIN euro2021.tb_country AS co ON ci.country_code=co.country_code
INNER JOIN euro2021.tb_match AS m ON ci.city_name=ci.city_name
INNER JOIN euro2021.tb_stadium AS st on st.stadium_code=m.stadium_code
GROUP BY ci.city_code, ci.city_name, co.country_code, co.country_name
Do you have any ideas or do you know if this was answered previously? Thanks in advance.

I think the problem is all in your JOIN clauses. I believe you want this:
SELECT ci.city_code,
ci.city_name,
co.country_code,
co.country_name,
COUNT(DISTINCT m.referee_code),
SUM(m.home_goals+m.visitor_goals)
FROM euri2021.tb_match AS m
INNER JOIN euro2021.tb_stadium AS st ON st.stadium_code=m.stadium_code
INNER JOIN euro2021.tb_city AS ci ON ci.city_code=st.city_code
INNER JOIN euro2021.tb_country AS co ON ci.country_code=co.country_code
IROUP BY ci.city_code, ci.city_name, co.country_code, co.country_name;

Related

Joining tables and sum by attribute

I need to know how many employees does each company have for each country they are present in? I have to join two tables (companies and cities) and sum number of employees for each country.
SELECT *, SUM(EMPLOYEES)
FROM COMPANIES WHERE
JOIN CITIES
ON COMPANIES.CITYNAME = CITIES.CITYNAME
doesn´t work...
Tables to join and sum employees for each country
A couple of tips,
Try to alias your joins so your not typing the full table names in your join statements
Add a group by on every field your not aggregating on for your sum to work
SELECT CI.COUNTRYNAME,CO.CITYNAME,CO.COMPANYNAME,CI.POPULATION,CI.COUNTRYNAME,
SUM(EMPLOYEES) AS TOTAL_EMPLOYEES
FROM COMPANIES AS CO
JOIN CITIES AS CI
ON CO.CITYNAME = CI.CITYNAME
GROUP BY CI.COUNTRYNAME,CO.COMPANYNAME,CO.CITYNAME,CI.POPULATION
https://rextester.com/DLZNT87647

SQL Get aggregate as 0 for non existing row using inner joins

I am using SQL Server to query these three tables that look like (there are some extra columns but not that relevant):
Customers -> Id, Name
Addresses -> Id, Street, StreetNo, CustomerId
Sales -> AddressId, Week, Total
And I would like to get the total sales per week and customer (showing at the same time the address details). I have come up with this query
SELECT a.Name, b.Street, b.StreetNo, c.Week, SUM (c.Total) as Total
FROM Customers a
INNER JOIN Addresses b ON a.Id = b.CustomerId
INNER JOIN Sales c ON b.Id = c.AddressId
GROUP BY a.Name, c.Week, b.Street, b.StreetNo
and even if my SQL skill are close to none it looks like it's doing its job. But now I would like to be able to show 0 whenever the one customer don't have sales for a particular week (weeks are just integers). And I wonder if somehow I should get distinct values of the weeks in the Sales table, and then loop through them (not sure how)
Any help?
Thanks
Use CROSS JOIN to generate the rows for all customers and weeks. Then use LEFT JOIN to bring in the data that is available:
SELECT c.Name, a.Street, a.StreetNo, w.Week,
COALESCE(SUM(s.Total), 0) as Total
FROM Customers c CROSS JOIN
(SELECT DISTINCT s.Week FROM sales s) w LEFT JOIN
Addresses a
ON c.CustomerId = a.CustomerId LEFT JOIN
Sales s
ON s.week = w.week AND s.AddressId = a.AddressId
GROUP BY c.Name, a.Street, a.StreetNo, w.Week;
Using table aliases is good, but the aliases should be abbreviations for the table names. So, a for Addresses not Customers.
You should generate a week numbers, rather than using DISTINCT. This is better in terms of performance and reliability. Then use a LEFT JOIN on the Sales table instead of an INNER JOIN:
SELECT a.Name
,b.Street
,b.StreetNo
,weeks.[Week]
,COALESCE(SUM(c.Total),0) as Total
FROM Customers a
INNER JOIN Addresses b ON a.Id = b.CustomerId
CROSS JOIN (
-- Generate a sequence of 52 integers (13 x 4)
SELECT ROW_NUMBER() OVER (ORDER BY a.x) AS [Week]
FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) a(x)
CROSS JOIN (SELECT x FROM (VALUES(1),(1),(1),(1)) b(x)) b
) weeks
LEFT JOIN Sales c ON b.Id = c.AddressId AND c.[Week] = weeek.[Week]
GROUP BY a.Name
,b.Street
,b.StreetNo
,weeks.[Week]
Please try the following...
SELECT Name,
Street,
StreetNo,
Week,
SUM( CASE
WHEN Total IS NULL THEN
0
ELSE
Total
END ) AS Total
FROM Customers a
JOIN Addresses b ON a.Id = b.CustomerId
RIGHT JOIN Sales c ON b.Id = c.AddressId
GROUP BY a.Name,
c.Week,
b.Street,
b.StreetNo;
I have modified your statement in three places. The first is I changed your join to Sales to a RIGHT JOIN. This will join as it would with an INNER JOIN, but it will also keep the records from the table on the right side of the JOIN that do not have a matching record or group of records on the left, placing NULL values in the resulting dataset's fields that would have come from the left of the JOIN. A LEFT JOIN works in the same way, but with any extra records in the table on the left being retained.
I have removed the word INNER from your surviving INNER JOIN. Where JOIN is not preceded by a join type, an INNER JOIN is performed. Both JOIN and INNER JOIN are considered correct, but the prevailing protocol seems to be to leave the INNER out, where the RDBMS allows it to be left out (which SQL-Server does). Which you go with is still entirely up to you - I have left it out here for illustrative purposes.
The third change is that I have added a CASE statement that tests to see if the Total field contains a NULL value, which it will if there were no sales for that Customer for that Week. If it does then SUM() would return a NULL, so the CASE statement returns a 0 instead. If Total does not contain a NULL value, then the SUM() of all values of Total for that grouping is performed.
Please note that I am assuming that Total will not have any NULL values other than from the RIGHT JOIN. Please advise me if this assumption is incorrect.
Please also note that I have assumed that either there will be no missing Weeks for a Customer in the Sales table or that you are not interested in listing them if there are. Again, please advise me if this assumption is incorrect.
If you have any questions or comments, then please feel free to post a Comment accordingly.

get the count of records from two tables in sql

I had three tables like ORG_DETAILS, DISTRICT_MASTER AND WORKER_DETAILS..
I want the count of number of organizations and related workers count based on district_name. here is the query i am trying......
SELECT dm.DISTRICT_NAME ,
count(od.Org_ID)as orgcount,
count(wd.WORKER_ID)as workerscount
from ORG_DETAILS od
left join WORKER_DETAILS wd on wd.ORG_ID = od.ORG_ID
left join DISTRICT_MASTER dm on od.DISTRICT_ID = dm.DISTRICT_ID
GROUP BY dm.DISTRICT_NAME
i am getting duplicate values in count like, one extra org count and worker count....
please help me with this...
thank you.....
The simplest way to fix your problem is to use count(distinct):
SELECT dm.DISTRICT_NAME ,
count(distinct od.Org_ID)a s orgcount,
count(distinct wd.WORKER_ID)as workerscount
from ORG_DETAILS od left join
WORKER_DETAILS wd
on wd.ORG_ID = od.ORG_ID left join
DISTRICT_MASTER dm
on od.DISTRICT_ID = dm.DISTRICT_ID
GROUP BY dm.DISTRICT_NAME;
For performance reasons, doing the aggregation along each dimension before the join performs better if the counts are high.

sql count with inner join

I have a table for absentees, and that table am storing the studentids of those who have been absent.
From this table I had to find total presentees and total absentees, for this I just joined the Sections table which contains the maximum capacity of particular Section.
For this my query was
select COUNT(Attendance.studentid) as Absentees
,Sections.Max-count(studentid) as Presentees
from Attendance
inner join Students
on students.StudentId=Attendance.StudentId
inner join Sections
on Sections.CourseId=students.CourseId
group by Sections.Max
Its working fine, the same way how can I find the gender wise presentees/absentees......gender column is in Students table, can anyone give me some idea, thanks in advance
Just add the gender column to your select ... columns and the group by, you'll end up with one row for each gender:
select COUNT(Attendance.studentid) as Absentees,
Sections.Max-count(studentid) as Presentees,
Students.Gender as Gender
from Attendance
inner join Students
on Students.StudentId=Attendance.StudentId
inner join Sections
on Sections.CourseId=Students.CourseId
group by Sections.Max, Students.Gender

Getting individual counts of a tables column after joining other tables

I'm having problems getting an accurate count of a column after joining others. When a column is joined I would still like to have a DISTINCT count of the table that it is being joined on.
A restaurant has multiple meals, meals have multiple food groups, food groups have multiple ingredients.
Through the restaurants id I want to be able to calculate how many of meals, food groups, and ingrediants the restaurant has.
When I join the food_groups the count for meals increases as well (I understand this is natural behavior I just don't understand how to get what I need due to it.) I have tried DISTINCT and other things I have found, but nothing seems to do the trick. I would like to keep this to one query rather than splitting it up into multiple ones.
SELECT
COUNT(meals.id) AS countMeals,
COUNT(food_groups.id) AS countGroups,
COUNT(ingrediants.id) AS countIngrediants
FROM
restaurants
INNER JOIN
meals ON restaurants.id = meals.restaurant_id
INNER JOIN
food_groups ON meals.id = food_groups.meal_id
INNER JOIN
ingrediants ON food_groups.id = ingrediants.food_group_id
WHERE
restaurants.id='43'
GROUP BY
restaurants.id
Thanks!
The DISTINCT goes inside the count
SELECT
COUNT(DISTINCT meals.id) AS countMeals,
COUNT(DISTINCT food_groups.id) AS countGroups,
COUNT(DISTINCT ingrediants.id) AS countIngrediants
FROM
restaurants
INNER JOIN
meals ON restaurants.id = meals.restaurant_id
INNER JOIN
food_groups ON meals.id = food_groups.meal_id
INNER JOIN
ingrediants ON food_groups.id = ingrediants.food_group_id
WHERE
restaurants.id='43'
GROUP BY
restaurants.id
You're going to have to do subqueries, I think. Something like:
SELECT
(SELECT COUNT(1) FROM meals m WHERE m.restaurant_id = r.id) AS countMeals,
(SELECT COUNT(1) FROM food_groups fg WHERE fg.meal_id = m.id) AS countGroups,
(SELECT COUNT(1) FROM ingrediants i WHERE i.food_group_id = fg.id) AS countGroups
FROM restaurants r
Where were you putting your DISTINCT and on which columns? When using COUNT() you need to do the distinct inside the parentheses and you need to do it over a single column that is distinct for what you're trying to count. For example:
SELECT
COUNT(DISTINCT M.id) AS count_meals,
COUNT(DISTINCT FG.id) AS count_food_groups,
COUNT(DISTINCT I.id) AS count_ingredients
FROM
Restaurants R
INNER JOIN Meals M ON M.restaurant_id = R.id
INNER JOIN Food_Groups FG ON FG.meal_id = M.id
INNER JOIN Ingredients I ON I.food_group_id = FG.id
WHERE
R.id='43'
Since you're selecting for a single restaurant, you shouldn't need the GROUP BY. Also, unless this is in a non-English language, I think you misspelled ingredients.