Group by Country with bridge table - sql

I am not quite sure with the tables below to write a query that returns countries that have communities. The CommunityLocation bridge table is where I can look for companies and their locations but not quite sure how to really mold this query.
Community
community_id
Location
location_id
country_id
Country
country_id
CommunityLocation
community_id
location_id
So I just need a list of country IDs that have communities.

I think this is what you need:
select distinct
c.country_id
from
countires c
join Location l
on c.country_id = l.country_id
join CommunityLocation cl
on cl.location_id = l.location_id

I would recommend exists:
select c.country_id
from countries c
where exists (select 1
from Location l join
CommunityLocation cl
on cl.location_id = l.location_id
where c.country_id = l.country_id
);
This avoids extra processing for removing duplicates -- and that is usually a performance gain.

Related

Query to display specific columns using a Set operator

I am trying to use a Set operator to show country names(Table A col.) without a city(Table B col.) and cities(B) without a country(A). I have also tried to write this query using LEFT JOINS, which I show below and I included Table C(Regions) because I am not sure whether to use that primary key in a LEFT JOIN.
Table A (Countries):
Column_name | Column_id
|
COUNTRY_ID | 1
COUNTRY_NAME | 2
REGION_ID | 3
Table B (Locations):
Column_name | Column_id
|
LOCATION_ID | 1
CITY | 4
COUNTRY_ID | 6
Table C(Regions):
Column_name | Column_id
|
REGION_ID | 1
I have tried the following:
SELECT c.country_name, l.city
FROM Countries c
LEFT JOIN Locations l ON c.country_id = l.country_id
UNION
SELECT c2.country_name, l2.city
FROM Countries c2
LEFT JOIN Locations l2 ON c2.country_id = l2.country_id;
The SQL statement above returned all Table A values, and Table A values that do not contain Table B values (Countries that do not have Cities).
I also tried this statement below and got the exact same result:
SELECT c.country_name, l.city
FROM Countries c
LEFT JOIN Locations l ON c.country_id = l.country_id
LEFT JOIN Regions r ON r.region_id = c.region_id;
The one thing it is missing is Table A values not found in Table B (Countries not found in Cities.)
There are a lot of options to get your desired result. One way is to use LEFT JOIN to get the countries without city and RIGHT JOIN to get the cities without country:
SELECT c.country_name, l.city
FROM countries c
LEFT JOIN locations l ON c.country_id = l.country_id
WHERE l.city IS NULL
UNION ALL
SELECT c.country_name, l.city
FROM countries c
RIGHT JOIN locations l ON c.country_id = l.country_id
WHERE c.country_name IS NULL;
Another possibility is to use two LEFT JOIN, but starting from the opposite table, like this:
SELECT c.country_name, l.city
FROM countries c
LEFT JOIN locations l ON c.country_id = l.country_id
WHERE l.city IS NULL
UNION ALL
SELECT c.country_name, l.city
FROM locations l
LEFT JOIN countries c ON c.country_id = l.country_id
WHERE c.country_name IS NULL;
If you don't like using JOIN at all, you can do this using NOT IN:
SELECT c.country_name, NULL city
FROM countries c
WHERE country_id NOT IN (SELECT country_id FROM locations)
UNION ALL
SELECT NULL country_name, l.city
FROM locations l
WHERE country_id NOT IN (SELECT country_id FROM countries);
Or if you prefer NOT EXISTS, this will work, too:
SELECT c.country_name, NULL city
FROM countries c
WHERE NOT EXISTS (SELECT 1 FROM locations WHERE country_id = c.country_id)
UNION ALL
SELECT NULL country_name, l.city
FROM locations l
WHERE NOT EXISTS (SELECT 1 FROM countries WHERE country_id = l.country_id);
I created an example that shows all these queries will produce the identic outcome: db<>fiddle
Add ORDER BY c.country_name and ORDER BY l.city to the queries in case you want the result set to be sorted by them.
A last, but important note: As you see, I used UNION ALL instead of UNION because I don't see a reason why to use UNION in your use case. UNION ALL is much faster, so I recommend to use that unless there is a really convincing reason to do not use it. The only advantage of UNION is that it does not show duplicate rows, but I think they are very unlikely in your situation, so it should not be required.
The simplest illustration of using a set operator to find countries without cities would be:
select country_id from countries
minus
select country_id from locations
COUNTRY_ID
----------
1
4
As you need the country name, you just need to look it up:
select country_name from countries
minus
select c.country_name from locations l
join countries c on c.country_id = l.country_id;
COUNTRY_NAME
-----------------
England
Italy
Cities without a country (or with an invalid country code) is simpler as a left join and filter:
select l.city, l.country_id
from locations l
left join countries c on c.country_id = l.country_id
where c.country_id is null
CITY
-----------------
Berlin
Tokyo
If the requirement really is to do this using set operators, you would (conceptually) look for cities whose country_id is in the set of (location countries minus city countries):
select l.location_id, l.city, l.country_id
from locations l
where l.country_id in
( select country_id from locations
minus
select country_id from countries )
However this wouldn't give you locations whose country_id was null.

Counting employees in country - user input not working

I'm writing query that returns number of people working in country. User have to input COUNTRY_ID which he is interested in.
I wrote down query that works fine, but only with hardcoded COUNTRY_ID.
SELECT COUNT(e.EMPLOYEE_ID)
FROM EMPLOYEES e
JOIN DEPARTMENTS d ON e.DEPARTMENT_ID = d.DEPARTMENT_ID
JOIN LOCATIONS l ON d.LOCATION_ID = l.LOCATION_ID
JOIN COUNTRIES c ON l.COUNTRY_ID = c.COUNTRY_ID
WHERE c.COUNTRY_ID = 'US';
With hardcoded COUNTRY_ID query returns 68 rows, with user input query returns 0 rows.
c.COUNTRY_ID = '&c.COUNTRY_ID'
Maybe I'm using this function wrongly, I would be glad for solution. Thanks.

GROUP BY with JOIN not giving correct result

How can I find the number of persons from each country
Person table
country_id, fName, lName
Countries table
country_id, country_name
I can find the number of persons from each country with their country IDs, using the below SQL statement
select
p.country_id, COUNT(p.country_id) as [Count]
from
persons p
GROUP BY
p.country_id
But what if I want to fetch country_name instead of country_id?
I tried this so far but it does not work as expected so I am missing something here.
select
c.country_name, COUNT(p.country_id) as [Count]
from
persons p INNER JOIN countries c ON p.country_id = c.country_id
GROUP BY
p.country_id
SQL Server 2000
GROUP BY the columns in the SELECT:
SELECT c.country_name, COUNT(p.country_id) as [Count]
FROM persons p INNER JOIN
countries c
ON p.country_id = c.country_id
GROUP BY c.country_name;
Notes:
Under some circumstances, you would want to include both the id and the name in the GROUP BY -- this would happen if two countries had the same name.
You can use COUNT(*) instead of COUNT(p.country_id). There is no problem counting rows instead of NULL-values.
If you wanted all countries, even those without people, then you would use an outer JOIN:
SELECT c.country_name, COUNT(p.country_id) as [Count]
FROM countries c LEFT JOIN
persons p
ON p.country_id = c.country_id
GROUP BY c.country_name;
You need to mention the column_name which is mentioned in select .
select
c.country_name, COUNT(p.country_id) as [Count]
from
persons p INNER JOIN countries c ON p.country_id = c.country_id
GROUP BY
c.country_name
You could use group by c.country_name .. the column you have in select and you use for refer the group by
select c.country_name, COUNT(p.country_id) as [Count]
from persons p INNER JOIN countries c ON p.country_id = c.country_id
GROUP BY c.country_name
The SQL statement is correct. There was some data corruption in the database due to which the results were not returned correctly.

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.

SQL troubling with multiple count in query

I already provide sql fiddle with schema and sample data.
http://sqlfiddle.com/#!2/e9d22/7/0
If I would like to know how many province and how many cities in Thailand.
Country Name | No. Provinces | No. Cities
Thailand | 77 | 1234
I guess that it need to use multiple COUNT(*) but I dont know how to use it.
Anybody know please suggest solution?
You need to use GROUP BY and COUNT:
SELECT c.name, count(distinct p.id) provincecoutn, count(distinct city.id) citycount
FROM country c
LEFT JOIN province p on c.id = p.country_id
LEFT JOIN City on p.id = city.province_id
GROUP BY c.name
Good luck.
Try this:
SELECT
C.Name, COUNT(DISTINCT P.Id) NoProvance, COUNT(CC.Id) NoCities
FROM country C
JOIN province P
ON C.Id = P.COUNTRY_ID
JOIN city CC
ON P.Id = CC.province_id
WHERE C.Name = 'Thailand'
GROUP BY C.Name
SQL FIDDLE DEMO
It's probably faster to count cities per province before joining to province:
SELECT c.name AS "Country Name"
,count(p.id) AS "No. Provinces"
,sum(ci.city_ct) AS "No. Cities"
FROM country c
LEFT JOIN province p ON p.country_id = c.id
LEFT JOIN (
SELECT province_id, count(*) AS city_ct FROM city GROUP BY 1
) ci ON ci.province_id = p.id
GROUP BY 1
-> sqlfiddle for PostgreSQL(!)