MySQL Multiple Group-By Count - mysql

I'm trying to count some results from a query. I've been through the various examples on here but none of them are adaptable enough to do what I'm trying to do.
If someone could take a look, point out where I'm going wrong, and kindly put me out of my misery, that would be wonderful. Thank-you.
Four Tables: Areas, Departments, Systems and Checks
Each Area has many Departments. Each Department has many Systems; and each System has many Checks. I'm trying to find out how many different Systems (by System_ID) have been checked in the previous week.
Current Query
Select
a.Area_Name,
d.Department_Name,
s.System_ID,
s.System_Name,
c.Check_Date
from departments d
left join areas a
on a.Area_ID = d.Department_Area
left join systems s
on s.System_Department = d.Department_ID
left join checks c
on c.Check_System_ID = s.System_ID
where Week(c.Check_Date) = Week(Now())
and Year(c.Check_Date) = Year(Now())
Returns the Following :
Area_Name | Department_Name | System_ID | System_Name | Check_Date
Area 1 Department 1 1 System 1 2017-02-27
Area 1 Department 1 1 System 1 2017-02-27
Area 1 Department 1 2 System 2 2017-02-27
Area 1 Department 2 3 System 3 2017-02-27
What I'm trying to get to will group the areas, departments, systems etc. To show a count of how many different systems have been checked in the week.
(I'm aware I haven't included any count or group-by in the query... I've left them out purposely to provide context. I have tried every possible way I can think but my knowledge is obviously lacking somewhere)
Desired Result
Area_Name | Department_Name | Count_Checks
Area 1 Department 1 2
Area 1 Department 2 1
As you can see, the desired result has grouped the two System 1 and treated them as 1 (as it's a single unique system), to show that over the last week, 2 different systems have been checked from department 1 and only 1 from department 2.
If anyone can shed some light... please do!
Thanks in advance for any help!

Use GROUP BY and aggregate using a combination of area/department as group:
SELECT a.Area_Name,
d.Department_Name,
COUNT(DISTINCT s.System_ID) AS Count_Checks
FROM departments d
LEFT JOIN areas a
ON a.Area_ID = d.Department_Area
LEFT JOIN systems s
ON s.System_Department = d.Department_ID
LEFT JOIN checks c
ON c.Check_System_ID = s.System_ID AND
WEEK(c.Check_Date) = WEEK(NOW()) AND
YEAR(c.Check_Date) = YEAR(NOW())
GROUP BY a.Area_Name,
d.Department_Name
One part of the query which might not be obvious is the code used to determine Count_Checks:
COUNT(DISTINCT s.System_ID)
This will count, for each area/department group, the number of distinct system ID values. In the case of Area 1/Department 1, this would yield a distinct system count of 2, while for Area 1/Department 2, this would give a count of just 1.

Related

Selecting everything from table 1 with average AND distinct values from table 2 and table 3

I am not entirely sure even how to name this post, because I do not know exactly how to ask it.
I have three tables. One with users, one with foods and one with the users rating of the foods, like such (simplified example):
Foods
id name type
---------------------
1 Apple fruit
2 Banana fruit
3 Steak meat
Users
id username
-----------------
1 Mark
2 Harrison
3 Carrie
Scores (fid = food id, uid = user id)
fid uid score
---------------------
1 1 3
1 2 5
2 1 2
3 2 3
Now, I have this query, which works perfectly:
SELECT fn.name as Food, ROUND(AVG(s.score),1) AS AvgScore FROM Foods fn LEFT JOIN Scores s ON fn.id = s.fid GROUP BY fn.id ORDER BY fn.name ASC
As you can tell, it lists all names from Foods including an average from all users ratings of the food.
I also want to add the unique users own score. (Assume that when Mark is logged in, his uid is set in a session variable or whatever)
I need the following output, if you are logged in as Mark:
Food AvgScore Your Score
Apple 4 3
I have made several attempts to make this happen, but I cannot find the solution. I have learned that if you have a question, it is very likely that someone else has asked it before you do, but I do not quite know how to phrase the question, so I get no answers when googling. A pointer in the right direction would be much appreciated.
You can try with case:
SELECT fn.name as Food,
ROUND(AVG(s.score),1) AS AvgScore,
sum(case s.uid = $uid when s.score else 0 end) as YourScore
FROM Foods fn
LEFT JOIN Scores s ON fn.id = s.fid
GROUP BY fn.id
ORDER BY fn.name ASC
$uid is variable off course.

sql select master records based on ANDing multiple detail records

I have master and detail tables as described below (with representative data). I want to select (mysql-compliant) Student.id for students that have an "A" in both Biology and Chemistry.
Students grades
id name id student_id class grade
1 ken 1 1 Biology A
2 beth 2 1 Chemistry A
3 joe 3 1 Math B
4 2 Biology A
5 2 Chemistry A
6 2 Math A
7 3 Biology B
8 3 Chemistry A
9 3 Math A
Currently, I'm just pulling in all the data into my program (java) but figure there's got to be a way in SQL to get the right records.
The results I'm looking for from the data above would be 1 & 2 (ken and beth). I've tried a few variations using joins and inner selects but can't quite get it to work. My main problem seems to be I'm ANDing my detail records eg., ...where grades.class='Biology' and grades.grade='A'
I took a look at SQL select from header table where detail table rows have multiple values but that didn't quite get me where I need to be.
Assistance greatly appreciated.
Try this
select s.id from students as s
inner join grades as g on s.id=g.student_id
group by s.id
having max(case when g.class='Biology' and g.grade='A' then 1 else 0 end)=1
and
having max(case when g.class='Chemistry' and g.grade='A' then 1 else 0 end)=1
Here is one way:
select g.student_id
from grades g
where g.class in ('Biology', 'Chemistry') and g.grade = 'A'
group by g.student_id
having count(distinct class) = 2;
Notes:
A join is not necessary because the grades table has the student id.
The where clause only gets records where a student has an 'A' in either class.
The having guarantees that a student has an 'A' in both classes.
I should note that there is an alternative method that uses join but not group by:
select gb.student_id
from grades gb join
grades gc
on gb.student_id = gc.student_id and
gb.class = 'Biology' and gc.class = 'Chemistry' and
gb.grade = 'A' and gc.grade = 'A';
This works -- and the performance might even be better. I like the group by and having approach because it is more flexible.

How to join multiple queries with different tables and different column name

I want to join multiple queries with different tables and column name, along with I need to display the count of duplicate fields as shown below.
The queries are: (Proj_uid is common in all the tables which I need to match)
select proj_name,Agency,District,Division,Proj_status from tempproj
Need to join 2 tables to get the result that is payment80 and payment20 which contains billtype column with duplicate values, I want to count those value too
SELECT p.Proj_name,p.billtype, COUNT(1) as CNT
FROM payment80 p where billtype='civil'
GROUP BY Proj_name, billtype
(This is by using single table but I want this result by joining both payment80 and payment20 tables)
SELECT p.Proj_name,p.billtype, COUNT(1) as CNT
FROM payment80 p where billtype='Electric'
GROUP BY Proj_name, billtype
(The billtype values I want to count and just display a number of duplicate records)
Proj_Name billtype
------------------------
policegruha civil
gruhayojna Electric
policegruha civil
dcoffice civil
spoffice Electric
dcoffice civil
3) Select billtype from payment, here also I need count the duplicate values and display in billtype
Duplicate values will be in billtype which contains some thing like this:
Finally I want an output like this:
Proj_name Agency District Division Projstatus Civilbilltype Electricbilltype
policegruha kumar chitradurga davangere ongoing 3 1
gruhayojna khan ballary ballary completed 2 2
Atered john bangalore bangalore ongoing 2 4
dcoffice ravi mangalore mangalore ongoing 1 2
spoffice mary chitradurga davangere completed 3 4
hostel jack ballary ballary completed 3 3
univercity kumar bangalore bangalore ongoing 4 2
mess Raj mysore mysore ongoing 2 1
policestation khan mysore mysore ongoing 1 4
conferencehall Rosy davangere davangere ongoing 2 2
You are joining three separate tables. One is physical, tempproj, and the other two are virtual: they are aggregates.
This is the technique.
SELECT p.proj_name,p.Agency,p.District,p.Division,p.Proj_status,
Civilbills.billcount as Civilbills,
Electribills.billcount as Electricbills
FROM tempproj p
LEFT JOIN (
SELECT Proj_name, COUNT(*) as billcount
FROM payment80
where billtype='civil'
GROUP BY Proj_name
) Civilbills ON Civilbills.Proj_name = p.proj_name
LEFT JOIN (
SELECT Proj_name, COUNT(*) as billcount
FROM payment80
where billtype='Electric'
GROUP BY Proj_name
) Electricbills ON Electricbills.Proj_name = p.proj_name
Your requirement includes two separate aggregates from the payment80 table. The LEFT JOINs prevent suppression of project rows that lack any bills of either category.

In MySQL, how to query multiple table counts on column when joining on common columns

I have two tables (they come from different original data sources)
Projects (cross sectional data)
Timestamp Project Owner Name Submission_Date
2014-02-18 1 Tim Susan 2014-02-10
2014-02-18 2 Matt Jaclyn 2014-02-10
2014-02-18 2 Tim Mary 2014-02-11
etc
and Hitups (activity log)
Project Owner Name Hitup_Date
1 Tim Susan 2014-02-01
2 Matt Jaclyn 2014-02-02
etc
And I want to run a query to get a count of activities from both tables, and grouped by the common project and owner. Given the above totally made up sample data, I'd expect to see results similar to
Project Owner count(Hitup_Date) count(Submission_Date)
1 Tim 1 1
2 Matt 1 1
2 Tim null 1
My attempt to query this is as follows
SELECT p.Project, p.Owner, COUNT(Hitup_Date), COUNT( p.Submission_Date )
FROM projects p, hitups h
WHERE p.project = h.project and p.owner = h.owner
AND p.date = ( SELECT MAX( DATE ) FROM projects )
GROUP BY p.project, p.owner
... which fails miserably. What's going wrong? I've searched exhaustively and have been unable to find prior examples on how to tackle a similar situation explicitly - thank you for your guidance.
You need to perform the grouping and aggregation first, and only then join the results:
SELECT p.project, p.owner, submission_count, hitup_count
FROM (SELECT project, owner, COUNT(*) AS submission_count
FROM projects
GROUP BY project, owner) p
LEFT OUT JOIN (SELECT project, owner, COUNT(*) AS hitup_count
FROM hitups
GROUP BY project, owner) h
ON p.project = h.project AND p.owner = h.owner

MySQL order by points from 2nd table

So I have MySQL 3 tables, items (which in this case are lodging properties and the data is simplified below), amenities that the properties might offer, and amenities_index which is a list of item ids and amenity ids for each amenity offered. The end user can select any number of amenities they want and I want to return the results in order of the number of amenities that match what they are looking for. So, if they search for 3 different amenities, I want the items listed that offer all 3, then those that offer 2, 1 and finally the rest of the items. I have a query that I think is working for getting the results in the correct order, but I was hoping that I could also return a point value based on the matches, and that's where I'm running into trouble. My SQL skills are a bit lacking when it comes to more complex queries.
Here is an example query I have that returns the results in the correct order:
SELECT * FROM items
ORDER BY
(
SELECT count(*) AS points
FROM `amenities_index`
WHERE
(amenity_id = 1 || amenity_id = 2)
AND amenities_index.item_id = items.id
) DESC
And here is what the tables are structured like. Any help is appreciated.
items table
id name
1 location 1
2 location 2
3 location 3
4 location 4
amenities table
id name
1 fireplace
2 television
3 handicapped accessible
4 kitchenette
5 phone
amenities_index
item_id amenity_id
1 2
1 3
1 5
2 1
2 2
2 6
3 2
3 3
3 4
3 5
You want to move your expression into the select clause:
SELECT i.*,
(SELECT count(*) AS points
FROM `amenities_index` ai
WHERE amenity_id in (1, 2) AND
ai.item_id = i.id
) as points
FROM items i
ORDER BY points desc;
You can also do this as a join query with aggregation:
SELECT i.*, ai.points
FROM items i join
(select ai.item_id, count(*) as points
from amenities_index ai
where amenity_id in (1, 2)
) ai
on ai.item_id = i.id
ORDER BY ai.points desc;
In most databases, I would prefer this version over the first one. However, MySQL would allow the first in a view but not the second, so it has some strange limitations under some circumstances.