creating possible combinations list from Many to Many relation - sql

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.

Related

What is the most efficient way of selecting data from relational database?

I just started working with databases and
I have this data sample from PostgreSQL tutorial
https://www.postgresqltutorial.com/postgresql-sample-database/
Which diagram looks like this:
I want to find all film categories rented in for example Canada. Is there a way of doing it without using SELECT within SELECT.. statement like this:
SELECT * FROM category WHERE category_id IN (
SELECT category_id FROM film_category WHERE film_id IN (
SELECT film_id FROM film WHERE film_id IN (
SELECT film_id FROM inventory WHERE inventory_id IN (
SELECT inventory_id FROM rental WHERE staff_id IN (
SELECT staff_id FROM staff WHERE store_id IN (
SELECT store_id FROM store WHERE address_id IN (
SELECT address_id FROM address WHERE city_id IN (
SELECT city_id FROM city WHERE country_id IN (
SELECT country_id FROM country WHERE country IN ('Canada')
)
)
)
)
)
)
)
)
)
I'm sure there must be something that i'm missing.
The proper way is to use joins instead of all these nested subqueries:
select distinct c.category_id, c.name
from category c
inner join film_category fc on fc.category_id = c.category_id
inner join inventory i on i.film_id = fc.film_id
inner join rental r on r.inventory_id = i.inventory_id
inner join staff s on s.staff_id = r.staff_id
inner join store sr on sr.store_id = s.store_id
inner join address a on a.address_id = sr.address_id
inner join city ct on ct.city_id = a.city_id
inner join country cr on cr.country_id = ct.country_id
where cr.country = 'Canada'
For your requirement you must join 9 tables (1 less than your code because the table film is not really needed as the column film_id can link the tables film_category and inventory directly).
Notice the aliases for each table which shortens the code and makes it more readable and the ON clauses which are used to link each pair of tables.
Also the keyword DISTINCT is used so you don't get duplicates in the results because all these joins will return many rows for each category.

Retrive counts of two columns from two diffrent tables with third table using join query in SQL

I have 3 tables: COUNTRY, STATE, CITY
This is my Country table with two columns:
CountryID, Name
This is my State table:
This is my City table:
I want to retrieve the count of states and cities according to the country table using join query.
Skipping the fact that your question is not asked well - try this query, it should work for you:
WITH
tab_a AS (
SELECT c.countryid, COUNT (s.stateid) AS state_num
FROM country c
LEFT JOIN state s ON c.countryid = s.countryid
GROUP BY c.countryid
),
tab_b AS (
SELECT c.countryid, COUNT (cc.cityid) city_num
FROM country c
LEFT JOIN state s ON c.countryid = s.countryid
LEFT JOIN city cc ON s.stateid = cc.stateid
GROUP BY c.countryid
)
SELECT a.countryid,
a.state_num,
b.city_num
FROM tab_a a JOIN tab_b b ON a.countryid=b.countryid

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

Getting Parent of Parent in Self Join Table

I have self join table. This table is being used to join up to 4 level, i.e.;
Region -> Country -> County -> Town
How can I get Parent of Parent of Town. To do this up to two level this is the query
SELECT t.ShortName AS Town,
(SELECT c.ShortName FROM Locations c
WHERE c.LocationId = t.ParentId) AS County
FROM Locations t
WHERE t.LocationId = 100
Now want to get Country which is parent of County.
Either hardcode another join or use a recursive CTE.
;with locs as
(
select 1 as level, ShortName, ParentId
from Locations
WHERE LocationId = 100
UNION ALL
SELECT level + 1, l.ShortName, l.ParentId
FROM Locations l
JOIN locs ON locs.ParentId = l.LocationId
)
SELECT * FROM locs;
Just pretend its 4 separate tables, using nicely named aliases:
SELECT town.ShortName as TownName,
county.ShortName as CountyName,
country.ShortName as CountryName,
region.ShortName as RegionName
FROM Locations town
INNER JOIN Locations county ON town.ParentID = county.LocationID
INNER JOIN Locations country ON county.ParentID = country.LocationID
INNER JOIN Locations region ON country.ParentID = region.LocationID
WHERE town.LocationID = 100
If not every town has a county, country, and region, then some of those might need to be LEFT OUTER joins.

how to combine 2 or more bridge tables in a single query

i have the following tables:
Table: People (id, first, last, age, phone, etc . .)
Table: Roles (id, name)
Table: Skills (id, name)
Table: People_Roles (id, personID^, roleID^)
Table: People_Skills (id, personID^, skillID^)
^ = foreign key
I basically want a query that gives me the full result set of all people and their roles and there skills.
Person.First, Person.Last, Roles.Name, Skills.Name
SELECT p.first, p.last, r.name, s.name
FROM People p
LEFT JOIN People_Roles pr
ON pr.personID = p.id
INNER JOIN Roles r
ON pr.roleID = r.id
LEFT JOIN People_Skills ps
ON ps.personID = p.id
INNER JOIN Skills s
ON ps.skillID = s.id
This query will select you all the people, even those without Roles or Skills assigned.
This query will work:
SELECT Person.First, Person.Last, Roles.Name, Skills.Name
FROM People
LEFT JOIN People_Skills
ON People.id = People_Skills.personID
INNER JOIN Skills
ON People_Skills.skillID = Skills.id
LEFT JOIN People_Roles
ON People.id = People_Roles.personID
INNER JOIN Roles
ON People_Roles.roleID = Skills.id
By the way, why your "bridge" tables have their own ID. I would just use a compound key made of PersonID and the corresponding foreign key (skillsID or rolesID) to ensure that every pair can only be made once.