Subquery not outputting the correct result? - mysql

I hope someone can help me with this problem, I been trying different combinations but can't seems to get the correct result.
First of all, I have 3 tables, category, department, and master, master has the FK. I want to get sum of total time for each department and sum of total time for category group by department so that I can see which department has how much time for that category, all should be group by department and in the correct order?
I can get the result but not coming out right, here is my code...
SELECT a.department, time_spent , COUNT(DISTINCT (a.department)), sum_quarantine FROM
(SELECT d.department, SUM(time_spent) as time_spent
FROM master as m
INNER JOIN department as d ON d.dept_id = m.dept_id GROUP BY d.department) AS a,
(SELECT d.department, c.category, SUM(time_spent) as sum_quarantine
FROM master as m
INNER JOIN category as c ON c.cat_id = m.cat_id
INNER JOIN department as d ON d.dept_id = m.dept_id
WHERE category = 'Quarantine' GROUP BY d.department) as b
GROUP BY b.department

What you want can be done without sub-query, making use of a CASE WHEN in a SUM, so that it only sums what you are interested in ('Quarantine'):
SELECT d.department,
SUM(CASE c.category
WHEN 'Quarantine' THEN time_spent
END) as sum_quarantine,
SUM(time_spent) time_spent,
COUNT(DISTINCT (c.category)) category_count
FROM master as m
INNER JOIN category as c ON c.cat_id = m.cat_id
INNER JOIN department as d ON d.dept_id = m.dept_id
GROUP BY d.department
ORDER BY 1
Here is fiddle.
Note that I added a count as well, since there was one in your attempt. But yours would always return 1, so I am not sure what you were looking for. I have added the number of categories linked to the department.
Also you did not specify the order you want, but it is easy to adapt the ORDER BY clause. The above query orders results by department, but if for instance you want to order by the second column in descending order, then do:
...
ORDER BY 2 DESC
If your results includes NULL values, and you prefer to have zeroes instead, then make use of the COALESCE function. For example:
COALESCE(SUM(time_spent), 0) time_spent,

Related

Join 2 different counts from 2 different tables into one subtable in sql

I'm having a problem in where i want to count how many medals in total a country has won from both the individual and team competitions does not give me the disered outcome. i have managed so far tocome up with this.
select distinct C.Cname as Country, count(i.medal) as Medals_Won
from individual_results as i, Country as C, participant as p
where (i.Olympian = p.OlympicID and C.Cname = p.country)
union
select distinct C.Cname, count(r.medal) as medals_Won
from team_results as r, Country as C, participant as p, team as t
where (r.team = t.TeamID and t.Member1 = p.OlympicID and C.Cname = p.Country)
group by C.Cname
order by medals_won desc
enter image description here
but i get this result.
even tho if i run the two separate pieces of code i ge the wanted restuls that is enter image description here
You say you can run your query and it gives you a result. This is bad. It indicates that you are MySQL's notorious cheat mode that lets you run invalid queries.
You have something like this:
select ...
union
select ...
group by ...
order by ...
There are two queries the results of which you glue together, namely
select ...
and
select ...
group by ...
So, your first query becomes:
select distinct C.Cname as Country, count(i.medal) as Medals_Won
from individual_results as i, Country as C, participant as p
where (i.Olympian = p.OlympicID and C.Cname = p.country)
You COUNT medals, i.e. you aggregate your data. And there is no GROUP BY clause. So you get one result row from all your data. You say you want to count all rows for which i.medal is not null. But you also want to select the country. The country? Which??? Is there just one country in the tables? And even then your query would be invalid, because still you'd have to tell the DBMS from which row to pick the country. You can pick the maximum country (MAX(C.Cname)) for instance or the minimum country (MIN(C.Cname)), but not the country.
The DBMS should raise an error on this invalid query, but you switched that off.
Make sure in MySQL to always
SET sql_mode = 'ONLY_FULL_GROUP_BY';
It is the default in more recent versions, so either you are working with old software or you switched from good mode to bad mode voluntarily.
And talking of old software: Even at the first moment MySQL was published, comma joins had long been deprecated. They were made redudant in 1992. Please don't ever use commas in your FROM clause. Use explicit joins ([INNER] JOIN, LEFT [OUTER] JOIN, etc.) instead.
As to the task, here is a straight-forward solution with joins:
select
c.cname as country,
coalesce(i.medals, 0) as medals_individual,
coalesce(t.medals, 0) as medals_team,
coalesce(i.medals, 0) + coalesce(t.medals, 0) as medals_total
from country c
left outer join
(
select p.country, count(ir.medal) as medals
from participant p
join individual_results ir on ir.olympian = p.olympicid
group by p.country
) i on on i.country = c.name
left outer join
(
select p.country, count(ir.medal) as medals
from participant p
join team t on t.member1 = p.olympicid
join team_results tr on tr.team = t.teamid
group by p.country
) t on on t.country = c.name
order by medals_total desc;
You should sum the union result for each of the subquery grouped by cname
select t.Cname , sum( t.Medals_Won)
from (
select C.Cname as Country, count(i.medal) Medals_Won
from individual_results i
inner join participant p ON i.Olympian = p.OlympicID
inner join Country C ON C.Cname = p.country
group by C.Cname
union
select distinct C.Cname, count(r.medal)
from team_results as r
inner join team as t ON r.team = t.TeamID
inner join participant as p ON t.Member1 = p.OlympicID
inner join Country as C ON C.Cname = p.Country
group by C.Cname
) t
group by t.Cname
order by sum( t.Medals_Won) desc

MYSQL View Query Performance Issue

I have 5 SQL tables
store
staff
departments
sold_items
staff_rating
I created a view that JOINs this four of the tables together. The last table (staff_rating),I want to get the rating column at a time close to when items was sold (sold_items.date) for the view rows.
I have tried the following SQL Queries which works but have performance issues.
SQL QUERY 1
SELECT s.name,
s.country,
d.name,
si.item,
si.date,
(SELECT rating
FROM staff_ratings
WHERE staff_id = s.id
ORDER BY DATEDIFF(date, si.date) LIMIT 1) AS rating,
st.name,
st.owner
FROM store st
LEFT OUTER JOIN staff s ON s.store_id = st.id
LFET JOIN departments d ON d.store_id = st.id
LEFT JOIN sold_items si ON si.store_id = st.id
SQL QUERY 2
SELECT s.name,
s.country,
d.name,
si.item,
si.date,
si.rating ,
st.name,
st.owner
FROM store st
LEFT OUTER JOIN staff s ON s.store_id = st.id
LFET JOIN departments d ON d.store_id = st.id
LEFT JOIN (SELECT *,
(SELECT rating
FROM staff_ratings
WHERE staff_id = si.staff_id
ORDER BY DATEDIFF(date, si.date) LIMIT 1) AS rating
FROM sold_items) si ON si.store_id = st.id
SQL Query 2 is faster than SQL Query 1. But Both still have performance issue. Appreciate help for a query with better performance. Thanks in advance.
Your query doesn't look right to me (as mentioned in a comment on the original post; lacking staff_id in the join on the sales, etc)
Ignoring that, one of your biggest performance hits is likely to be this...
ORDER BY DATEDIFF(date, si.date) LIMIT 1
That order by can only be answered by comparing EVERY record for that staff member to the current sales record.
What you ideally want to be able to do is find the appropriate staff rating from an index, and not to have to run computations that involve dates from both the ratings table and the sales table.
If, for example, you wanted "the most recent rating BEFORE the sale", the query can be substantially improved...
SELECT
s.name,
s.country,
d.name,
si.item,
si.date,
(
SELECT sr.rating
FROM staff_ratings sr
WHERE sr.staff_id = s.id
AND sr.date <= si.date
ORDER BY sr.date DESC
LIMIT 1
)
AS rating,
st.name,
st.owner
FROM store st
LEFT JOIN staff s ON s.store_id = st.id
LFET JOIN departments d ON d.store_id = st.id
LEFT JOIN sold_items si ON si.store_id = st.id
Then, with an index for staff_ratings(staff_id, date, rating) the optimiser can very quickly look up which rating to use, without having to scan Every Single Rating for that staff member.
Why DATEDIFF? Would something like this work better? If so, the given index will make it work much faster.
WHERE staff_id = s.id
AND s.date >= s1.date
ORDER BY s.date
LIMIT 1
And INDEX(staff_id, date)
Do you need LEFT JOIN? Perhaps plain JOIN?
d may benefit from INDEX(store_id, name)

MySQL - Group orders count by clients numbers

I want to group order's count to show how many clients have that number of orders.
I have come up with:
select count(*) as quantidade_pedidos, clientes.id
from pedidos
inner join clientes
on pedidos.cliente_id = clientes.id
where pedidos.aprovado = 1
group by quantidade_pedidos
but I just can't group by 'quantidade_pedidos' anyway.
Is there any way to group by a temporary column? Another way of doing this? show how many clients (number) have that number of orders placed?
Example
8 orders placed -> 3 clients have 8 orders placed
etc
Your original query is wrong. You need to group by clientes.id:
select count(*) as quantidade_pedidos, c.id
from pedidos p inner join
clientes c
on p.cliente_id = c.id
where p.aprovado = 1
group by c.id;
In an aggregation query, the unaggregated columns go in the group by, not the aggregated ones.
Also note that table aliases make the query easier to write and to read.
As for the question in the first line, use a subquery:
select quantidade_pedidos, count(*)
from (select count(*) as quantidade_pedidos, c.id
from pedidos p inner join
clientes c
on p.cliente_id = c.id
where p.aprovado = 1
group by c.id
) x
group by quantidade_pedidos;
But given that the query in the question doesn't work, I'm not sure this is what you really want to do.

MySQL query issue

This is my query:
SELECT count(*) as total, dp.name,dp.id,dp.description, dp.avatar
FROM `doctors` d
right join departments dp on d.department_id = dp.id
group by d.department_id
I have to tables: doctors and departments. I want to extract the total number of doctors from each department. This query works fine, it returns me all of the deparments, which have a doctors, but not which does not have. Somehow I want to show all of the departements and a total, which represents the doctors whose belong to a department. How can i do that ?
This is the doctor table:
and this is the departments table
You can give this a try:
SELECT
(SELECT count(*) FROM doctors d WHERE d.department_id = dp.id) AS total,
dp.*
FROM departments AS dp
And if you want to use JOIN then try this:
SELECT
COUNT(d.department_id) AS total,
dp.*
FROM departments AS dp
LEFT JOIN doctors AS d ON dp.id = d.department_id
GROUP BY dp.id
SELECT (SELECT count(*) FROM doctors AS d WHERE d.id = dp.id) as total, dp.name,dp.id,dp.description, dp.avatar
FROM departments dp
there are multiple ways of doing this ofcourse, I would do it like this
i dont think you need to join you just need a count of all the doctors and you dont do anything with the rest of the information
In my couple of decades working with SQL I've never used right joins. I've always found the LEFT join easier to read. I also try and return the columns in order of the highest to lowest level of detail and finish with sum's and counts. It reads a lot better.
Try this:
SELECT dp.id,dp.name,dp.description, dp.avatar,count(*) as total
FROM departments dp
LEFT JOIN doctors d
on d.department_id = dp.id
GROUP BY dp.id,dp.name,dp.description, dp.avatar
You must always group by every column within your select clause except those that are an aggregation (e.g. sum,count) or a constant.

Count on joined table causes return of 1 row

I've got query like:
SELECT
b.title,
b.url,
b.`date`,
b.gallery,
count(c.id) as comments_count,
a.name,
b.content,
b.comments,
LEFT(b.content, LOCATE('<page>', b.content)-1) as content_short
FROM blog b
LEFT JOIN blog_comments c ON
(b.id = c.note AND c.approved = 1)
LEFT JOIN administrators a ON
(b.aid = a.id)
WHERE
b.`date` < now() AND
b.active = 1
ORDER BY b.`date` DESC;
Now, when I remove count(c.id) as comments_count,, I've got 2 rows returned. When it's present, there's only 1 row returned.
Is there some way to fix ot or I simply have to change
count(c.id) as comments_count, to (select count(id) ascomments_countfrom blog_comments where note = b.id) as comments_count,?
Count(*) is an aggregated function, so it will apply in a group.
That means that when you count on groups, it will apply the function on every group.
The groups are formed when you use Group By, in this case, you're not using, so MySQL consider that ALL select (your joins) is ONLY 1 GROUP.
So, applies the count on the unique group and returning the count of rows.
you should add a Group by by the field you want
An example is here