How to sort by custom count with postgresql? - sql

I have two tables:
Companies: (id, name)
Workers: (id, name, country)
I would like to get all companies and sort them by numbers of employees from a given country.
For a query looking for companies that have more workers from the USA, the result should give:
#workers from USA | company id | company name
----------------------------------------------
90 6 foo corp
45 9 bar corp
0 3 foobar corp
I tried:
select
count(w.country='USA') as mycount,
w.company_id,
c.company_name
from
companies c
left join workers w on
c.id=w.company_id
group by
w.company_id,
c.company_name
order by mycount desc;
But that's counting all workers regardless of their countries.
Any ideas?

You can do that easily with a correlated subquery:
SELECT
(SELECT count(*) FROM workers w WHERE w.company_id=c.id AND w.country='USA') AS mycount,
c.id,
c.company_name
FROM
companies c
ORDER BY mycount DESC

Does replacing COUNT(w.country='USA') with COUNT(*) help? I believe your query needs to refer to fields on the Companies table as opposed to workers because your left join leaves open the possibility that the set of workers pulled for a specific company/country is the empty set.
Try:
select count(*) as mycount,
c.company_id,
c.company_name
from companies c
left join workers w on c.id=w.company_id
group by c.company_id, c.company_name
order by mycount desc;

Related

Display courses with at least 10 students

I have the following tables:
Students (id, name, surname, study_year, department_id)
Courses(id, name)
Course_Signup(id, student_id, course_id, year)
I want to display the courses to which at least 10 students have signed up for, only using subqueries (no group-by, join or set operations). This could be easily implemented using data aggregation and join:
SELECT c.name, COUNT(csn.course_id)
FROM Course_Signup csn
JOIN Courses c
ON csn.course_id = c.id
GROUP BY c.name
HAVING COUNT(csn.course_id) >= 10
But how would I do this only using subqueries? Is there any other way, other than COUNT, to get the number of courses? Thank you, in advance!
You can use a correlated sub-query to retrieve the name:
SELECT (SELECT c.name FROM Courses c WHERE csn.course_id = c.id) AS name,
COUNT(*)
FROM Course_Signup csn
GROUP BY
course_id
HAVING COUNT(*) >= 10
Note: you should also GROUP BY the primary key the uniquely identifies the course as there may be two courses with the same name.
If you also don't want to use GROUP BY then:
SELECT name
FROM Courses c
WHERE 10 <= ( SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id )
or, to also get the number of sign-ups:
SELECT *
FROM (
SELECT name,
( SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id ) AS num_signups
FROM Courses c
)
WHERE num_signups >= 10;
You could do:
SELECT c.name
FROM Courses c
WHERE (
SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id
) >= 10
which only uses a subquery and has no group-by, join or set operations.
fiddle
If you wanted the actual count in the result set then you would need to repeat the subquery in the select list.
You might also need to do COUNT(DISTINCT cs.student_id) if there might be duplicates; particularly if the same student can sign up in multiple years - but then you might want to restrict to a single year anyway.

How could I use the select statement on feature from a subquery ? (Postgree)

I'm training for an interview and trying to solve a query, I would like to find for each city who is the client who spent the most. I got the good result the max spent by city but I get an error when I'm trying to retrieve the name and lastname of my customer who spent this amount. Is there an efficient way to do it ? Thank you!
select max(total_payment),X.city, X.firstname, X.lastname
from (
select sum(amount) as total_payment, c.customer_id, cit.city_id, cit.city as city, c.first_name as firstname, c.last_name as lastname
from payment p
inner join customer as c on p.customer_id=c.customer_id
inner join address as ad on c.address_id=ad.address_id
inner join city as cit on ad.city_id=cit.city_id
group by c.customer_id, cit.city_id
order by city
) as X
group by X.city
Target result column:
The name and last name of the customer who spent the most for each city.
120,Paris,Nicolas, Dupont
130, Madrid, Raul, Garcia
70, London,Dave, Goldman
You want window functions:
select cc.*
from (select sum(p.amount) as total_payment, c.customer_id, cit.city_id,
cit.city as city, c.first_name as firstname, c.last_name as lastname,
row_number() over (partition by cit.city order by sum(p.amount) desc) as seqnum
from payment p join
customer c
on p.customer_id = c.customer_id join
address ad
on c.address_id = ad.address_id join
city cit
on ad.city_id = cit.city_id
group by c.customer_id, cit.city_id
) cc
where seqnum = 1;
Note that your query has two errors that should fail any interview:
You are using ORDER BY in a subquery. According to the standard and most databases, ORDER BY is either not allowed or ignored.
In your outer query, the GROUP BY columns are inconsistent with the unaggregated SELECT columns. Once again, this violates the standard and most databases return a syntax error.

How to use COUNT and MAX together but groupwise?

How do I display the project name with the second highest number of employees?
Relation schema, with primary key and foreign key as applicable:
Employee(Eid,Ename,Address,city,Doj,salary) Project(Pid,Location,Pname,Mng,Client,Branch)
Works(Eid,Pid)
Eid and Pid in Works are ForeignKey.
eid and pid are primary keys in employee and project respectively
select count (*) , pname
from project natural join work
group by pname;
But this will only give the count of employees according to the project... Not the maximum number of employees
select max as ex1
from (Select count(*) as ex1
from works
where PID in ( select distinct pid from works);
I am trying this in linux - oracle
OK,I think the SQL below would return what you want(The number 2 project's name and employee numbers):
select * from
(
select
p.pname,
count(1) as cc
from
work w
join
project p on w.pid = p.pid
join
employee e on w.eid = e.eid
group by
p.pname
order by cc desc limit 2
) tmp order by cc asc limit 1
SELECT COUNT(W.Eid) AS TotalEmployees, P.pname
FROM Works W
INNER JOIN Project P ON W.Pid = P.Pid
GROUP BY P.Pid
ORDER BY TotalEmployees desc
OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY

How to sort by count with postgresql?

I have two tables:
Companies: (id, name, city)
Workers: (id, name)
I would like to get all companies and sort them by numbers of employes.
The result should give:
count | company id | company name | city
------------------------------------------
90 6 foo corp NY
45 9 bar corp LA
0 3 foobar corp HO
I tried:
select
c.*,
count(w.id) as c
from
companies c
left join
workers w
on
c.id = w.company_id
group by
c.id
order by
c desc;
But that's not working as it tells me to group by g.name too :/
Any ideas?
You've aliased the table and column as the same thing, so don't do that. It's not invalid, just tough to follow.
Anyway, include all columns that you're selecting that aren't aggregates in your group by:
select
count(w.id) as mycount,
w.company_id,
c.company_name,
c.city
from
companies c
left join workers w on
c.id=w.company_id
group by
w.company_id,
c.company_name,
c.city
order by mycount desc;
If you don't want the count result to be returned (because of an ORM framework or so), you could apply it directly in the order by clause:
select
c.*
from
companies c
left join
workers w
on
c.id = w.company_id
group by
c.id
order by
count(w.id) desc;
Tested with postgreSQL 11
Try this as a subquery:
SELECT C.*
FROM
(
SELECT C.Id, C.Company_Name, C.City, COUNT(W.Id) AS CNT
FROM Companies C
LEFT JOIN Workers W ON W.Company_Id = C.Id
GROUP BY C.Id, C.Company_Name, C.City
) T
ORDER BY T.CNT

SQL question about counting

I want to make a query so that I can grab only Locations that have at least 50 Places.
I have a table of Locations:
Id, City, Country
1, Austin, USA
2, Paris, France
And a table of Places connected to Locations by Location_id
Id, Name, Details, Location_id
1, The Zoo, blah, 2
2, Big Park, blah, 2
I can join them like so:
SELECT places.name, places.id, locations.country, locations.city
FROM places
INNER JOIN locations
ON places.location_id = locations.id
by how can I only get the results of cities that have at least 50 places and order them by the largest amount?
Thanks!
Use a GROUP BY with a HAVING clause.
SELECT locations.country, locations.city, COUNT(*)
FROM places
INNER JOIN locations ON places.location_id = locations.id
GROUP BY locations.country, locations.city
HAVING COUNT(*) >= 50
OK I've seen that the above answers are almost there but have some mistakes, so just posting the correct version:
SELECT locations.country, locations.city, COUNT(*) as count_of_places
FROM places
INNER JOIN locations ON places.location_id = locations.id
GROUP BY locations.country, locations.city
HAVING COUNT(*) >= 50
ORDER BY count_of_places;
You can use the having clause to limit these rows by the value of an aggregate column. Also, MySQL allows you to use lazy group bys, so you can absolutely take advantage of this:
select
l.country,
l.city,
p.name,
p.details,
count(*) as number_of_places
from
locations l
inner join places p on
l.id = p.location_id
group by
l.id
having
count(*) >= 50
order by
number_of_places,
l.country,
l.city,
p.name
Somewhat unperformant, but should work:
SELECT places.name, places.id, sq.country, sq.city, sq.counter
FROM (SELECT Locations.id, country,city, count(*) as counter FROM Locations
JOIN Places ON (Locations.Id=Places.Location_id)
GROUP BY locations.id HAVING count(*) >= 50) AS sq
JOIN Places ON (sq.id=Places.Location_id)
ORDER BY counter DESC`
P.S. The exact syntax may vary depending on the database, I'm not sure if this is mysql-compatible as is.