Selecting cities that have 10 or more students and instructors combined in SQL - sql

I need to show the city, state, number of student residents, number of instructor residents, and total student/instructor residents in that city. The information is contained in 3 tables: ZIPCODE, STUDENT, and INSTRUCTOR.
The ZIPCODE table has the columns ZIP, CITY, and STATE.
The STUDENT table has STUDENT_ID and ZIP.
The INSTRUCTOR table has INSTRUCTOR_ID and ZIP.
I've tried a couple of inner joins, and intersects, but I keep getting a wide variety of errors. I'm still very new with SQL, and am not sure how to actually make this work, any help or advice would be greatly appreciated.

You probably want a mix of union and join for this. I doubt you want intersect. Plenty of ways to do this, here's one
SELECT
Z.city,
Z.state,
SUM(case when d.typ = 's' then 1 ELSE 0 END) as count_students,
SUM(case when d.typ = 'i' then 1 ELSE 0 END) as count_instructors,
Count(*) as count_all
FROM
(SELECT * FROM
(SELECT 's' as typ, zip FROM student)
UNION ALL
(SELECT 'I ' as typ, zip FROM Instructor)
) d
INNER JOIN
zipcode z
ON d.zip on z.zip
GROUP BY
z.city, z.state
I pull all the records out of each student and instructor table and union them to make one big list, make a column to keep track of the type, the sum does the counting, when the type is s, the case when returns a 1. The sum will sum the 1s up as a count. You thus end up with a city/state/typ combination for each row and when grouped on city and state and summed on the typ, it gives a count
Here's another way to do this:
SELECT
Z.city,
Z.state,
SUM(s.ct) as count_students,
SUM(i.ct) as count_instructors,
SUM(s.ct) + SUM(I.ct) as count_all
FROM
zipcode z
LEFT OUTER JOIN
(SELECT zip, count(*) ct FROM student GROUP BY zip) s
ON s.zip = z.zip
LEFT OUTER JOIN
(SELECT zip, count(*) as ct FROM Instructor GROUP BY zip) i
ON i.zip = z.zip
GROUP BY z.city, z.state
We group and count the students and the instructors in their own subqueries producing just a single count per zip and join these (left join) to all the zip codes. We group in a sub query to ensure that there is only ever a 1:1 relationship between zipcode and s/i. If it were 1:many the sums would beome distorted. Because multiple zips can refer to one city there is another round of grouping and summing to aggregate all the zips from one city

Related

Postgres Question: Aren't both a and b correct?

For questions below, use the following schema definition.
restaurant(rid, name, phone, street, city, state, zip)
customer(cid, fname, lname, phone, street, city, state, zip)
carrier(crid, fname, lname, lp)
delivery(did, rid, cid, tim, size, weight)
pickup(did, tim, crid)
dropoff(did, tim, crid)
It's a schema for a food delivery business that employs food carriers (carrier table).
Customers (customer table) order food from restaurants (restaurant table).
The restaurants order a delivery (delivery table); to deliver food from restaurant to customer.
The pickup table records when carrier picks up food at restaurant.
The dropoff table records when carrier drops off food at customer.
1.Find customers who have less than 5 deliveries.
a. select cid,count()
from delivery
group by cid
having count() < 5;
b. select a.cid,count()
from customer a
inner join delivery b
using(cid)
group by a.cid
having count() < 5;
c. select a.cid,count()
from customer a
left outer join delivery b
on a.cid=b.cid
group by a.cid
having count() < 5;
d. select cid,sum(case when b.cid is not null then 1 else 0 end)
from customer a
left outer join delivery b
using (cid)
group by cid
having sum(case when b.cid is not null then 1 else 0 end) < 5;
e. (write your own answer)
No, they are not correct. They miss customers who have had no deliveries.
The last is the best of a bunch of not so good queries. A better version would be:
select c.cid, count(d.cid)
from customer c left outer join
delivery d
on c.cid = d.cid
group by c.cid
having count(d.cid) < 5;
The sum(case) is over kill. And Postgres even offers a better solution than that!
count(*) filter (where d.cid is not null)
But count(d.cid) is still more concise.
Also note the use of meaningful table aliases. Don't get into the habit of using arbitrary letters for tables. That just makes queries hard to understand.

Count city and State from 2 different tables

I need to count the number of cities occurrences on 2 different tables. One Table is the Supplier table which has supplier_id, City and State. The second table is consumer with consumer id , City and State. My query needs to return the State, City number of supplier cities and number of consumer cities where the names match.
I have tried a couple different things like intersect and union all but cant get it My latest is below but it is Not sure what I am doing wrong
SELECT S.State
,C.City
,count(S.City) as Number_Of_Suppliers
,count(C.City) as Number_Of_Consumers
from dbo.Tb_Supplier S
left outer Join dbo.Tb_Consumer C
On S.STATE = C.STATE
AND S.City = C.City
group by S.state
,C.City
Use union all and group by:
select state, city, sum(supplier)
sum(supplier) as Number_Of_Suppliers,
sum(consumer) as Number_Of_Consumers
from ((select state, city, 1 as supplier, 0 as consumer
from dbo.Tb_Supplier s
) union all
(select state, city, 0 as supplier, 1 as consumer
from dbo.Tb_Consumer c
)
) sc
group by state, city

SELECT * FROM table in addition of aggregation function

Short context:
I would like to show a list of all companies except if they are in the sector 'defense' or 'government' and their individual total spent on training classes. Only the companies that have this total amount above 1000 must be shown.
So I wrote the following query:
SELECT NAME, ADDRESS, ZIP_CODE, CITY, SUM(FEE-PROMOTION) AS "Total spent on training at REX"
FROM COMPANY INNER JOIN PERSON ON (COMPANY_NUMBER = EMPLOYER) INNER JOIN ENROLLMENT ON (PERSON_ID = STUDENT)
WHERE SECTOR_CODE NOT IN (SELECT CODE
FROM SECTOR
WHERE DESCRIPTION = 'Government' OR DESCRIPTION = 'Defense')
GROUP BY NAME, ADDRESS, ZIP_CODE, CITY
HAVING SUM(FEE-PROMOTION) > 1000
ORDER BY SUM(FEE-PROMOTION) DESC
Now what I actually need is, instead of defining every single column in the COMPANY table, I would like to show ALL columns of the COMPANY table using *.
SELECT * (all tables from COMPANY here), SUM(FEE-PROMOTION) AS "Total spent on training at REX"
FROM COMPANY INNER JOIN PERSON ON (COMPANY_NUMBER = EMPLOYER) INNER JOIN ENROLLMENT ON (PERSON_ID = STUDENT)
WHERE SECTOR_CODE NOT IN (SELECT CODE
FROM SECTOR
WHERE DESCRIPTION = 'Government' OR DESCRIPTION = 'Defense')
GROUP BY * (How to fix it here?)
HAVING SUM(FEE-PROMOTION) > 1000
ORDER BY SUM(FEE-PROMOTION) DESC
I could define every single column from COMPANY in the SELECT and that solution will do the job (as in the first example), but how can I make the query shorter using "SELECT * from the table COMPANY"?
The key idea is to summarize in the subquery to get the total spend for the company. This allows you to remove the aggregation from the outer query:
select c.*, pe.total_spend
from company c join
sector s
on c.sector_code = s.code left join
(select p.employer, sum(e.fee - e.promotion) as training_spend
from person p join
enrollment e
on p.person_id = e.student
group by p.employer
) pe
on pe.employer = c.company_number
where s.sector not in ('Government', 'Defense') and
pe.total_spend > 1000

Select single row or multiple rows based on condition

I'm trying to identify a student's home district by joining a student's zip code to a district zip code. A given district may overlap several zip codes, so several possible home districts may appear for the student. For example, Zip code 99999 may include the Houston and Sugarland school districts. I can narrow down the home district to a single record when the student's city has the same name as the district name as for example if the student's city is Houston and the district name is Houston. In that case, I only want to retrieve the Houston district, not both Houston and Sugarland. However, if the student happened to live in Bayou with the zip code of 99999, then I'd want to retrieve both Houston and Sugarland districts since I don't have a fix on the district. I've tried several approaches but cannot come up with a solution. Here's
a primitive attempt:
Select S.Name, S.City, S.Zip, D.DistrictName
From tblStudent S
Left Join tblDistrict D on D.zip=S.zip
Where
(Case
When D.DistrictName=S.City then D.DistrictName
Else D.DistrictName
End)=D.DistrictName
Any suggestions are greatly appreciated!
You can try selecting each case separately and then making a union of the two queries.
Case 1: District Name equals City Name
Case 2: There is no District Name that is equal to the City Name
Something like this:
Select S.Name, S.City, S.Zip, D.DistrictName
From tblStudent S
Inner Join tblDistrict D on D.zip=S.zip and D.DistrictName = S.City
Union
Select S.Name, S.City, S.Zip, D.DistrictName
From tblStudent S
Inner Join tblDistrict D on D.zip=S.zip
Where not exists (
Select D2.* from tblDistrict D2
Where D2.DistrictName = S.City
And D2.zip = S.zip
)
SQL Fiddle: http://sqlfiddle.com/#!9/06d6d7/2/0

sql query to select matching rows for all or nothing criteria

I have a table of cars where each car belongs to a company. In another table I have a list of company locations by city.
I want to select all cars from the cars table whose company has locations on all cities passed into the stored procedure, otherwise exclude those cars all together even if it falls short of one city.
So, I've tried something like:
select id, cartype from cars where companyid in
(
select id from locations where cityid in
(
select id from cities
)
)
This doesn't work as it obviously satisfies the condition if ANY of the cities are in the list, not all of them.
It sounds like a group by count, but can't make it work with what I tried.
I"m using MS SQL 2005
One example:
select id, cartype from cars c
where ( select count(1) from cities where id in (...))
= ( select count(distinct cityid)
from locations
where c.companyid = locations.id and cityid in (...) )
Maybe try counting all the cities, and then select the car if the company has the same number of distinct location cities are there are total cities.
SELECT id, cartype FROM cars
WHERE
--Subquery to find the number of locations belonging to car's company
(SELECT count(distinct cities.id) FROM cities
INNER JOIN locations on locations.cityid = cities.id
WHERE locations.companyId = cars.companyId)
=
--Subquery to find the total number of locations
(SELECT count(distinct cities.id) FROM cities)
I haven't tested this, and it may not be the most efficient query, but I think this might work.
Try this
SELECT e.*
FROM cars e
WHERE NOT EXISTS (
SELECT 1
FROM Cities p
WHERE p.location = e.Location
)