I am struggling with a SQL query,
Query:
I want to find a list of hospitals with a count of dentists (is_denitist=true) and all doctors (including dentists) having monthly_income > 100 000
I have 2 tables Hospitals and Doctors with the following schema,
-------------
| Hospital |
|-----------|
| id | name |
|-----------|
---------------------------------------------------------
| Doctor |
|--------------------------------------------------------
| id | name | monthly_income | is_dentist | hospital_id |
|--------------------------------------------------------
The query I came up with is,
select h.name, count(d.is_dentist), sum(d.monthly_income)
from Hospital h inner join Doctor d
on h.id = d.hospital_id
where d.monthly_income > 100000 and d.is_dentist=true
group by h.name;
If I am a dentist and having income less than 100 000 then the hospital should still count me as a dentist.
But the caveat in the above query is it filters out all doctors having monthly_income above 100 000 and are dentists. I want an independent count of these conditions like predicates over each count() column. How can we achieve this in a single query?
You can do conditional aggregation.
Since is_dentist (presumably) contains 0/1 values, you can just sum() this column to count how many doctors belong to the group.
On the other hand, you can use another conditional sum() to count how many doctors have an income above the threshold.
select
h.name,
sum(d.is_dentist) no_dentists,
sum(d.monthly_income > 100000) no_doctors_above_100000_income
from Hospital h
inner join Doctor d on h.id = d.hospital_id
group by h.name;
You have two independent conditions (monthly_income > 100000, and is_dentist=true) which means there are two different data sets. You can't be used two different data set in the same group query.
So you need to divide it into two subqueries. You can check the following query whether the result is you wanted:
select temp3.name, temp1.dentist_count, temp2.income_count from
(select d1.hospital_id, count(*) as dentist_count from Doctor d1 where d1.monthly_income>100000 group by d1.hospital_id) as temp1
join
(select d2.hospital_id, count(*) as income_count from Doctor d2 where d2.is_dentist=true group by d2.hospital_id) as temp2
on temp1.hospital_id=temp2.hospital_id
join
(select h.id, h.name from Hospital h) as temp3
on temp2.hospital_id=temp3.id;
Related
I am new to SQL.
I checked "another solutionSQL JOIN two tables with AVG" posted in StackOverflow. And I don't get the meaning with this line in that article:AVG(score.score * 1.0) Besides, the alternative solution below doesn't work at all:
SELECT songs.id, songs.song, songs.artist,
(SELECT AVG(Score) FROM score WHERE score.id = songs.id) AS AvgScore)
FROM songs
Here are my tables:
[employees]
Dep ID | SALARY
1 | 500
2 | 200
1 | 300
2 | 1000
2 | 400
3 | 200
3 | 300
[departments]
Dept ID Dep| Dept Name
1 | Volcano
2 | ShootingStar
3 | Tsunami
In the end, I want to create a list looks like:
Dept Name | Average Salary
Volcano | $$$
ShootingStar| $$
Tsunami | $$$$
I tried various ways and hunting hints in stackoverflow for sub queries/inner join features but still can't get it.
Based on the solution in the previous link SQL JOIN two tables with AVG, this code works:
-- mapping DEPT ID with NAME + average salary by DEPT --
select EMPLOYEES.DEP_ID, DEPARTMENTS.DEP_NAME, AVG(EMPLOYEES.SALARY) as AVG_S
from EMPLOYEES
LEFT JOIN DEPARTMENTS
ON EMPLOYEES.DEP_ID = DEPARTMENTS.DEPT_ID_DEP
group by DEP_ID, DEP_NAME;
However, I want to understand the reason WHY my original one doesn't work?
select E.DEP_ID, D.DEP_NAME, (select AVG(SALARY) from EMPLOYEES group by DEP_ID) as AVG_S
from EMPLOYEES E, DEPARTMENTS D
where E.DEP_ID = D.DEPT_ID_DEP
group by DEP_ID, DEP_NAME;
Please help!
Thank you very much.
The query you wanted to write:
select
e.dep_id,
d.dep_name,
(select avg(salary) from employees e where e.dep_id = d.dept_id_dep) as avg_s
from departments d;
The logic of the query is to select from departments only, then use a correlated subquery to compute the average salaries of employees of the department. This avois aggregating in the outer query.
Your query fails in the following regards:
table employees is in the from clause of the outer query
the outer query does group by
the subquery is not corelated on the department
I have a table called booking_details.
id | tour_id | tour_fee| booking_id
1 | 1 | 200 | 1
2 | 2 | 350 | 1
3 | 1 | 200 | 2
4 | 2 | 350 | 3
tour_id refers to the Tours table and the booking_id refers Bookings table.
I want to get a report like this
tour_id 1 refers to New york tour
tour_id 2 refers to Paris tour
I need a generate a report something like this
tour name | total_income | number_of_bookings
New york tour| 400 | 2
Paris tour | 700 | 2
Here basicaly tour name, total income from that tour and number of bookings for that tour.
What I have done upto now is this. But this gives me a syntax error. It seems I can't group by results.
SELECT booking_details.*,Tours.name as name, count(Tours.id) FROM booking_details
inner join Tours on
booking_details.tour_id = Tours.id group by Tours.name;
How do I achive this using MySQL?
you have used aggregation count() in your query and from your requirement, it shows you need aggregation. when you used aggregation you have to put selection column in group by also
SELECT Tours.name as name,sum(tour_fee) income, count(Tours.id)
FROM booking_details
inner join Tours on
booking_details.tour_id = Tours.id group by Tours.name
As you used in selection booking_details.* which means every column of booking table but you have not put those column in group by so it thrown error
You are trying to select non aggregated columns which are not part of your GROUP BY clause.
Change your query like following.
SELECT t.NAME AS NAME,
Sum(bd.tour_fee) total_income,
Count(t.id) number_of_bookings
FROM booking_details bd
INNER JOIN tours t
ON bd.tour_id = t.id
GROUP BY t.NAME;
Small suggestion, as a good practice you should use alias names for tables when joining.
You need to add all other columns in group by except aggregated fields
SELECT
booking_details.tour_id,
Tours.name AS name,
SUM(tourfee) AS total_income,
COUNT(Tours.id)
FROM
booking_details
INNER JOIN
Tours ON booking_details.tour_id = Tours.id
GROUP BY
booking_details.tour_id, Tours.name
I am making queries to extract data from database which holds customer order. There's one table which holds customer id's and the customer's name. Another table which has the order id, customer id of who placed the order, a quantity of the item bought, and an item id. The last table holds the item id's and item names. I am trying to sort these to show an individual's most popular purchase, but am having issues properly grouping and ordering to produce the correct result, below is an example of what is intended.
customers
1 | John
---+-----
2 | Jane
orders
1 | 2 | 4 | 1
---+---+---+---
2 | 2 | 5 | 2
---+---+---+---
3 | 2 | 2 | 1
---+---+---+---
4 | 1 | 1 | 2
items
1 | Chair
---+-------
2 | Sofa
After properly sorting and grouping, the output table should like:
John | Sofa
------+------
Jane | Chair
Currently I can connect the item names to the purchaser and return a random item bought, but not the most popular by quantity. I have tried entering multiple fields into group by and managed to properly group the items by name and sort by quantity, but in doing so the customer id's became ungrouped. Been trying to solve this for days so any help would be appreciated. Please note that this is a very simplified version of the actual problem where many more tables are involved, including multiple items table which are being joined together to one.
You should use group by on joined tables
select
b.name
, c.name
, sum(quantity) as tot
from orders as a
inner join Customers as b on a.customer_id = b.id
inner join Items as c on a.item_id = c.id
group by b.name, c.name
order by tot
Selecting the sum of the quantities per customer-item group is easy, but selecting the top seller is a bit harder.
The first step is the query to get all the groups with the sums of the quantities for each customer-item:
SELECT
customer_name,item_name,SUM(quantity)
FROM
orders o
JOIN customers c ON o.customer_id=c.id
JOIN items i ON o.item_id=i.id
GROUP BY customer_name,item_name;
Then to only select the groups with the maximum quantity sums we use some trickery:
SELECT
customer_name,item_name,SUM(quantity),
(SELECT SUM(quantity) AS qmax
FROM
orders o2
JOIN customers c2 ON o2.customer_id=c2.id
JOIN items i2 ON o2.item_id=i2.id
WHERE c2.id=c.id
GROUP BY c2.customer_name,i2.item_name
ORDER BY qmax DESC LIMIT 1) AS qmax
FROM
orders o
JOIN customers c ON o.customer_id=c.id
JOIN items i ON o.item_id=i.id
GROUP BY customer_name,item_name
HAVING SUM(quantity)=qmax;
Edit:
Here's a link to a fiddle: SQLFiddle
I have two tables:
booking - records the order detail
id | booking_amount
-------------------
1 | 150
2 | 500
3 | 400
payment - records the payment for order
id | booking_id | amount
------------------------
1 | 1 | 100
2 | 1 | 50
2 | 2 | 100
I want to find all bookings where the payments are not complete. With the above data, we expect the answer to be 2,3, because the sum of payments for booking_id=1 matches the corresponding booking_amount in the booking_table.
To answer your question, you have 2 things you need to think about :
you want the total amount in your table payment by every booking row
you want to join your booking_amount table with payment.
Part 1 is quite simple:
SELECT sum(amount) as TotalP, booking_id FROM payment GROUP BY booking_id
Just a basic query with a simple aggregate function...
For part 2, we want to join booking_amount and payment; the basic JOIN would be:
SELECT * FROM booking b
LEFT JOIN payment p ON b.id = p.booking_id
We do a LEFT JOIN because we may have some booking who are not in the payment table. For those bookings, you will get NULL value. We will use a COALESCE to replace the NULL values by 0.
The final query is this:
SELECT b.id, COALESCE(TotalP, 0), b.booking_amount
FROM
booking b
LEFT JOIN
(SELECT sum(amount) as TotalP, booking_id FROM payment GROUP BY booking_id) as T
ON b.id = T.booking_id
WHERE COALESCE(TotalP, 0) < b.booking_amount
You need to use a outer join to combine your two tables and look for your conditions. Also, you will need to use SUM(..) function to get the sum of the amount for each id in the payment table.
Please try this:
select b.id from booking b
left outer join -- cant be inner join because we lose id:3 in that case.
(
select booking_id, SUM(amount) as Total
from payment group by booking_id
) p on b.id = p.booking_id
where b.booking_amount > Coalesce(Total,0) --Coalesce is required for such values coming NULL, like id:3, they will be assigned as 0.
Suppose I have these two tables:
|Customers | |Purchases |
---------------- -----------------
|CusID |----- |PurchaseID |
|CusName | |---<|CusID |
|CusAge | |ItemName |
|CusDateAdded | |DatePurchased |
I am needing to filter my result set by multiple ItemNames. Say I want to return all Customers who are between the ages of 18 and 24 who purchased a 'camera' AND a 'phone'. I can run the following query to obtain all records for customers between the age range:
SELECT CusID
FROM Customers AS C
JOIN Purchases AS P
ON C.CusID = P.CusID
WHERE C.CusAge BETWEEN 18 AND 24
However when it comes time to filter on the ItemName column in Purchases table how does one filter on multiple rows? Supposing it is possible without multiple queries?
SELECT C.CusID
FROM Customers AS C
JOIN Purchases AS P ON C.CusID = P.CusID
WHERE C.CusAge BETWEEN 18 AND 24
AND ItemName IN ('camera','phone')
GROUP BY C.CusID
HAVING count(distinct ItemName) = 2
Group by the customer and return only those having both items.
I believe this will answer your question: SQL FIDDLE
SELECT C.CustID,C.CustName,P.ItemName
FROM Customers AS C
JOIN Purchases AS P ON C.CustID = P.CustID
GROUP BY C.CustID,C.CustName,P.ItemName
HAVING P.ItemName IN ('camera','phone')