How can I join multiple tables for count? - sql

I have 3 tables named houses, trees, rivers. All of these tables have city_id column. I want to group total counts by cities. Cities are in another table.
My database is postgresql.
city_name trees houses rivers
City-1 1000 200 1
City-2 300 100 2
City-3 4000 210 4
I can get for trees
SELECT
city.name as city_name,
count(*) as trees
FROM trees as t, cities as city
WHERE t.city_id = city.city_id
GROUP BY city.name
But I could not join three tables in sama query.

To avoid issues with duplication of rows in a JOIN it's probably easiest to do the aggregation in subqueries and then JOIN them:
SELECT c.name,
COALESCE(t.cnt, 0) AS trees,
COALESCE(h.cnt, 0) AS houses,
COALESCE(r.cnt, 0) AS rivers
FROM cities c
LEFT JOIN (SELECT city_id, COUNT(*) AS cnt
FROM trees
GROUP BY city_id) t ON t.city_id = c.city_id
LEFT JOIN (SELECT city_id, COUNT(*) AS cnt
FROM houses
GROUP BY city_id) h ON h.city_id = c.city_id
LEFT JOIN (SELECT city_id, COUNT(*) AS cnt
FROM rivers
GROUP BY city_id) r ON r.city_id = c.city_id
We use a LEFT JOIN in case a given city has no trees, houses or rivers.
Demo on dbfiddle

An alternative to Nick's answer:
SELECT
city.name as city_name,
count(distinct t.id) as trees,
count(distinct h.id) as houses,
count(distinct r.id) as rivers
FROM cities as city
left join trees as t on t.city_id = city.city_id
left join rivers as r on r.city_id = city.city_id
left join houses as h on h.city_id = city.city_id
GROUP BY city.name
--
Not sure of the performance implications specifically with Postgres, but here's a (fairly old, so things might have moved on since) article suggesting count(distinct) can be slow in Postgres, together with options:
postgresql COUNT(DISTINCT ...) very slow

Related

Why Oracle OFFSET and LIMIT is not working when using DISTINCT

I need to get clarified the below situation.
I have a city, country table and I need to validate this will some other tables and get the city country results ordered by the country. Here's my query for that
SELECT distinct
c.code as CITY ,
c.country as COUNTRY from location_info li
inner join someTable s on li.loc_id = s.some_id
inner join city c on s.city = c.code
ORDER BY c.country
And this provides the results as
Now when I use OFFSET and LIMIT values in the below query
SELECT distinct
c.code as CITY,
c.country as COUNTRY from location_info li
inner join someTable s on li.loc_id = s.some_id
inner join city c on s.city = c.code
ORDER BY c.country OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY.
I expect to get ADOR, ADPC, ADSJ, ADVD, ALV as the output.
Why is that and what I am missing here in this query.
When I do the following I get the expected outcome
SELECT CITY, COUNTRY FROM ( SELECT distinct
c.code as CITY,
c.country as COUNTRY from location_info li
inner join someTable s on li.loc_id = s.some_id
inner join city c on s.city = c.code
ORDER BY c.country ) OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY.
The issue is that the ORDER BY is not stable. What that means is that you have ties in the key values. So, running the query two times might result in different orderings.
This is easily fixed by including more keys in the order by so the order by keys uniquely identify each row:
select distinct c.code as CITY, c.country as COUNTRY
from location_info li join
someTable s
on li.loc_id = s.some_id join
city c
on s.city = c.code
order by c.country, c.code;

UNION vs. JOIN, b and c not defined? (sqlite3)

select city, prov, a, b, c from (
select count(city) as a, city, prov from locations left outer join rides on src=lcode
group by city
union
select count(city) as b, city, prov from locations left outer join rides on dst=lcode
group by city
union
select count(city) as c, city, prov from locations l left outer join enroute e on l.lcode=e.lcode
group by city
) group by city;
So I got an error saying "no such column: b" (and also column c). Is it possible to solve this without changing a lot of this query?
Your subquery is returning three columns, a, city, and prov. That is, for a union/union all query, the column names come from the first subquery. There is no b or c.
Presumably, you want some sort of JOIN, not UNION ALL. However, your question does not explain what you are trying to do. And it doesn't have sample data or desired results. So, it is really hard to say what you actually want.
It strikes me that you actually want:
select city, prov, sum(a), sum(b), sum(c)
from ((select city, prov, count(city) as a, null as b, null as c
from locations left outer join
rides on src = lcode
group by city
) union all
(select city, prov, null, count(city), 0
from locations left outer join
rides
on dst = lcode
group by city
) union all
(select city, prov, null, null, count(city)
from locations l left outer join
enroute e
on l.lcode = e.lcode
group by city
)
) abc
group by city;
The expressions that are not encapsulated within an aggregate function and must be included in the GROUP BY clause. For Unions, all of the select statements in those unions must have the same column names.
Below would do the required tasks.
select * from (
select count(city) as cityCount, prov from locations left outer join rides on src=lcode
group by prov
union
select count(city) as cityCount, prov from locations left outer join rides on dst=lcode
group by prov
union
select count(city) as cityCount,prov from locations l left outer join enroute e on l.lcode=e.lcode
group by prov
);
Doesn't require an additional groupby for Outside subquery. It will already be grouped by provinces of locations.

creating possible combinations list from Many to Many relation

I have a Set of tables
Hotels
Countries
Regions
Cities
Hotel_Types
and a many to many relations table named Mappings which contains all the relations/mappings which contains info like
id, hotel_id, reference_type, reference_id, ...
where reference_type can be a Country, Region, City, Hotel_Type etc
and reference_id is the id of said entity like country_id or city_id etc.
I need to create a list of all possible combinations of
Country_Name+Hotel_Type_Name
Region_Name+Hotel_Type_Name
City_Name+Hotel_Type_Name
Where the hotels exist. Any help how may I access the names from different tables and how to combine them
I am implying few things here but you could do inner joins in this way:
select name, hotel_type_name
from (select c.country_name as name, h.hotel_type_name Mappings m inner join Countries c on m.reference_type='Country' and m.reference_id=c.country_id inner join hotel_Types h on m.reference_type='Hotel_type' and m.reference_id=h.hotel_type_id) union all
(select c.region_name as name, h.hotel_type_name Mappings m inner join Regions r on m.reference_type='Region' and m.reference_id=r.region_id inner join Hotel_Types h on m.reference_type='Hotel_type' and m.reference_id=h.hotel_type_id) union all
(select c.city_name as name, h.hotel_type_name Mappings m inner join Cities ci on m.reference_type='City' and m.reference_id=ci.city_id inner join Hotel_Types h on m.reference_type='Hotel_type' and m.reference_id=h.hotel_type_id)
This will list unique combinations of Country_Name+Hotel_Type_Name
--link hotels to hotel_type
with Hotel_Hotel_Types as (
select h.hotel_id
,ht.reference_id as hotel_types_id
from Hotels as h
inner join Mappings ht on ht.reference_type = 'Hotel_Type' and h.hotel_id = ht.hotel_id
)
--link hotels to Country_Name
,Hotel_Country_Name as (
select h.hotel_id
,c.reference_id as countries_id
from Hotels as h
inner join Mappings c on c.reference_type = 'Country' and h.hotel_id = c.hotel_id
)
select distinct ht.*, c.*
from Hotel_Hotel_Types hht
inner join Hotel_Types ht on ht.hotel_types_id = hht.hotel_types_id
inner join Hotel_Country_Name hc on hc.hotel_id = hht.hotel_id
inner join Countries c on с.countries_id = hc.countries_id
Region_Name+Hotel_Type_Name and City_Name+Hotel_Type_Name can be queried using similar sqls.

Nested 'Where'?

I have a table named Actor, with only a column for City (CityId). I want to return the number of actors in a particular State (StateId). The catch however is that I have separate tables for City, County, and finally State (City has CountyId, County has StateId). How do I this in a T-SQL query?
I have a solution that involves nested Select statements, something like:
SELECT COUNT(1)
FROM Actor a
WHERE a.CityId IN
(SELECT CityId FROM City WHERE CountyId IN...)
...but is there a more efficient way to do this? Thanks
You can use this query to get your output
----------------------------------------------------------
SELECT COUNT(ActorId)
FROM Actor a
INNER JOIN City c ON a.cityId = c.cityId
INNER JOIN Country con ON c.countryId = con.countryId
INNER JOIN STATE s ON con.stateId = s.stateId
GROUP BY s.stateId
Use JOINS to query your data.
I am using INNER JOIN here.
Assuming that you have CountryId in your City Table, You can do it following way.
In case you don't have countryId in your City Table you have to apply one more INNER JOIN on State Table.
SELECT COUNT(1) FROM Actor a INNER JOIN
City b ON a.CityId = b.CityId
WHERE b.CountryId IN (...)
You can easily put the JOINS across different table that you have and then use the Group By clause to find out the total number of actors from specific state.
I have used the column name on the basis of my wild guess, you can change them with the original name that you have in your database.
SELECT StateId,
Count(ActorId) AS Total
FROM ACTOR
INNER JOIN City ON Actor.CityId = City.CityId
INNER JOIN County ON County.CountyId = City.CountyId
INNER JOIN State ON State.StateId = County.StateId
GROUP BY State.StateId
Assuming the relation names, you can do something like this with joins:
select s.ID, s.Name, count(*)
from Actors a
inner join Cities c on c.ID = a.CityID
inner join County cn on cn.ID = c.CountyID
inner join State s on s.ID = cn.StateID
group by s.ID, s.Name
If you only need the StateId you don't even need to join with states, this will do:
select cn.StateID, count(*)
from Actors a
inner join Cities c on c.ID = a.CityID
inner join County cn on cn.ID = c.CountyID
group by cn.StateID

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.