SQL join on multiple levels - sql

I'm struggle with a SQL join for parent client records and just posted a query, but I realised after posting it that my example was slightly wrong (but still a useful post) so i've created a new one, that's more accurate :)
If i have the following database structure:
Table Regions
|Region_no | Region_Level | owning_region_no |
| 1 | 1 | |
| 2 | 2 | 1 |
| 3 | 2 | 1 |
| 4 | 3 | 2 |
| 5 | 3 | 2 |
| 6 | 3 | 3 |
Table Postcodes
| Postcode | Region_no |
| PO32 3AE | 4 |
| PO32 3AA | 5 |
| PO32 3AF | 6 |
Table UnitsMappings
| Unit_No | region_no |
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 2 | 1 |
| 3 | 3 |
| 3 | 3 |
| 4 | 6 |
| 4 | 5 |
Table Units
| Unit_no | Unit_Name |
| 1 | South |
| 2 | SouthEast |
| 3 | Central |
| 4 | SouthWest |
[Updated sample answer]
What I really want, is the following:
| Unit_name | Postcode |
| South | PO32 3AE |
| South | PO32 3AA |
| South | PO32 3AF |
| SouthEast | PO32 3AE |
| SouthEast | PO32 3AA |
| SouthEast | PO32 3AF |
Even though South has only regions 1 and 2 mapped to it, the level 3's are mapped to the level 2's and then 1's (and the postcodes are mapped to the level 3's)
Now the difficulty is, that i just want the postcodes (from the postcode tables) which are associated to the level 3 regions levels in regions. So there may be a region level 1 associated to a unit_no, but i need all of the postcodes at the bottom that are mapped to the level 3.

There is no valid unit name in you test data for the region in region level 3.
The region with region level 3 are 4, 5 and 6.
There is no unit associated with region 4, the unit associated with region 5 and 6 is the unit 4, but in the table unit_name there is no unit 4.
If you have the data then this query will get the data
SELECT pc.postcode, u.unit_name
FROM regions reg
INNER JOIN Postcodes pc ON reg.region_no = pc.region_no
INNER JOIN UnitsMappings um ON reg.region_no = um.region_no
INNER JOIN Units u ON um.unit_no = u.unit_no
WHERE reg.region_level = 3
changing the join on Units from INNER to LEFT will get you the postcodes with NULL as unit_name with the test data.

based on your details here's my mapping ,
unit => units_mapping
units_mapping => (Postcodes or regions) // assuming using all table we use postcodes
Postcodes => regions
regions (main)
select reg.region_no, Units.name , pc.postcodes
from regions reg,
Postcodes pc,
UnitsMappings UM,
Units units
where reg.region_no = pc.region_no and
pc.region_no = UM.region_no and
UM.Unit_No = Units.Unit_no
order by reg.region_no asc
if there is null in one table, the record won't appear ...
to handle use inner join
not yet tested ..... just assume :)

Try this,
Select u.Unit_name,pc.Postcode from Units as u
inner join UnitsMappings um on u.Unit_no = um.Unit_no
inner join Postcodes as pc on pc.Region_no = um.region_no

Related

SQL - joining 3 tables and choosing newest logged entry per id

I got rather complicated riddle to solve. So far I'm unlocky.
I got 3 tables which I need to join to get the result.
Most important is that I need highest h_id per p_id. h_id is uniqe entry in log history. And I need newest one for given point (p_id -> num).
Apart from that I need ext and name as well.
history
+----------------+---------+--------+
| h_id | p_id | str_id |
+----------------+---------+--------+
| 1 | 1 | 11 |
| 2 | 5 | 15 |
| 3 | 5 | 23 |
| 4 | 1 | 62 |
+----------------+---------+--------+
point
+----------------+---------+
| p_id | num |
+----------------+---------+
| 1 | 4564 |
| 5 | 3453 |
+----------------+---------+
street
+----------------+---------+-------------+
| str_id | ext | name |
+----------------+---------+-------------+
| 15 | | Mein st. 33 | - bad name
| 11 | | eck st. 42 | - bad name
| 62 | abc | Main st. 33 |
| 23 | efg | Back st. 42 |
+----------------+---------+-------------+
EXPECTED RESULT
+----------------+---------+-------------+-----+
| num | ext | name |h_id |
+----------------+---------+-------------+-----+
| 3453 | efg | Back st. 42 | 3 |
| 4564 | abc | Main st. 33 | 4 |
+----------------+---------+-------------+-----+
I'm using Oracle SQL. Tried using query below but result is not true.
SELECT num, max(name), max(ext), MAX(h_id) maxm FROM history
INNER JOIN street on street.str_id = history._str_id
INNER JOIN point on point.p_id = history.p_id
GROUP BY point.num
In Oracle, you can use keep:
SELECT p.num,
MAX(h.h_id) as maxm,
MAX(s.name) KEEP (DENSE_RANK FIRST ORDER BY h.h_id DESC) as name,
MAX(s.ext) KEEP (DENSE_RANK FIRST ORDER BY h.h_id DESC) as ext
FROM history h INNER JOIN
street s
ON s.str_id = h._str_id INNER JOIN
point p
ON p.p_id = h.p_id
GROUP BY p.num;
The keep syntax allows you to do "first()" and "last()" for aggregations.

Select all rows where rows in another joined table match condition

So I want to select all rows where a subset of rows in another table match the given values.
I have following tables:
Main Profile:
+----+--------+---------------+---------+
| id | name | subprofile_id | version |
+----+--------+---------------+---------+
| 1 | Main 1 | 4 | 1 |
| 2 | Main 1 | 5 | 2 |
| 3 | Main 2 | ... | 1 |
+----+--------+---------------+---------+
Sub Profile:
+---------------+----------+
| subprofile_id | block_id |
+---------------+----------+
| 4 | 6 |
| 4 | 7 |
| 5 | 8 |
| 5 | 9 |
+---------------+----------+
Block:
+----------+-------------+
| block_id | property_id |
+----------+-------------+
| 7 | 10 |
| 7 | 11 |
| 7 | 12 |
| 7 | 13 |
| 8 | 14 |
| 8 | 15 |
| 8 | 16 |
| 8 | 17 |
| ... | ... |
+----------+-------------+
Property:
+----+--------------------+--------------------------+
| id | name | value |
+----+--------------------+--------------------------+
| 10 | Description | XY |
| 11 | Responsible person | Mr. Smith |
| 12 | ... | ... |
| 13 | ... | ... |
| 14 | Description | XY |
| 15 | Responsible person | Mrs. Brown |
| 16 | ... | ... |
| 17 | ... | ... |
+----+--------------------+--------------------------+
The user can define multiple conditions on the property table. For example:
Description = 'XY'
Responsible person = 'Mr. Smith'
I need all 'Main Profiles' with the highest version which have ALL matching properties and can have more of course which do not match.
It should be doable in JPA because i would translate it into QueryDSL to build typesafe, dynamic queries with the users input.
I already searched trough all questions regarding similar problems but couldn't project the answer onto my problem.
Also, I've already tried to write a query which worked quite good but retrieved all rows with at least one matching condition. Therefore i need all properties in my set but it only fetched (fetch join, which is missing in my code examplte) the matching ones.
from MainProfile as mainProfile
left join mainProfile.subProfile as subProfile
left join subProfile.blocks as block
left join block.properties as property
where mainProfile.version = (select max(mainProfile2.version)from MainProfile as mainProfile2 where mainProfile2.name = mainProfile.name) and ((property.name = 'Description' and property.value = 'XY') or (property.name = 'Responsible person' and property.value = 'Mr. Smith'))
Running my query i got two rows:
Main 1 with version 2
Main 2 with version 1
I would have expected to get only one row due to mismatch of 'responsible person' in 'Main 2'
EDIT 1:
So I found a solution which works but could be improved:
select distinct mainProfile
from MainProfile as mainProfile
left join mainProfile.subProfile as subProfile
left join subProfile.blocks as block
left join block.properties as property
where mainProfile.version = (select max(mainProfile2.version)from MainProfile mainProfile2 where mainProfile2.name = mainProfile.name)
and ((property.name = 'Description' and property.content = 'XY') or (property.name = 'Responsible person' and property.content = 'Mr. Smith'))
group by mainProfile.id
having count (distinct property) = 2
It actually retrieves the right 'Main Profiles'. But the problem is, that only the two found properties are getting fetched. I need all properties though because of further processing.

Oracle query using 3 tables, sql

I have a problem with a query for Oracle with this scenario:
Table People
ID | Name
1 | juan
2 | pedro
3 | luis
Table Properties
ID | nombre_inmueble | FK to Table People
1 | house | 1
2 | garden | 1
3 | terrace | 1
4 | moto | 2
5 | jet | 2
Table Accessories
ID | accessories | FK Table Properties
1 | windows | 1
2 | doors | 1
3 | scale | 2
4 | plants | 3
5 | motor | 4
What I want is only the people who have Properties and that have ALL Accessories, in this case the output would be
1 | juan
What would be the query?
Your query will look like this:
SELECT *
FROM People P
WHERE EXISTS(
SELECT 1
FROM Properties T
WHERE T.PEOPLE= P.ID
)
AND NOT EXISTS(
SELECT 1
FROM Properties T
WHERE T.PEOPLE= P.ID
AND NOT EXISTS(
SELECT 1
FROM Accessories A
WHERE A.Properties = T.ID
)
);

Combining two view into one result set with transform?

I have a couple of views that generates the following two outputs in SQL Server.
First one (Flats output) shows the number of flats in a particular town with Tileroofs and Brickwalls. Second one shows the same, but for houses.
What I'm trying to do is to create a final table that looks like the 3rd example where the flats and house counts are combined with the corresponding Tileroof and Brickwall combinations.
I have tried union and then grouping, but I'm really struggling to get the Flats and Houses count columns side by side. Is anyone able to help please?
Thanks
--View one
| Town | Flats | TileRoofs | Brick Wall |
-----------------------------------------
| A | 3 | Y | N |
| A | 4 | N | Y |
| A | 8 | N | N |
--View two
| Town | Houses | TileRoofs | Brick Wall |
------------------------------------------
| A | 1 | Y | Y |
| A | 2 | Y | N |
| A | 5 | N | Y |
| A | 2 | N | N |
--Prefered output, by combining the two--
| Town | Flats | Houses | TileRoofs | Brick Wall |
--------------------------------------------------
| A | 0 | 1 | Y | Y |
| A | 3 | 2 | Y | N |
| A | 4 | 5 | N | Y |
| A | 8 | 2 | N | N |
Full outer join might help here.
select isnull(a.Town, b.Town) Town,
isnull(a.TileRoofs, b.TileRoofs) TileRoofs,
isnull(a.[Brick wall], b.[Brick wall]) [Brick wall],
isnull(a.Flats, 0) Flats,
isnull(b.Houses, 0) Houses
from ViewOne a
full outer join ViewTwo b
on a.Town = b.Town
and a.TileRoofs = b.TileRoofs
and a.[Brick wall] = b.[Brick wall]
select
v2.Town ,coalesce(v1.flat,0) as flat,v2.houses,v2.TileRoofs, v2.Brick, v2.Wall
from
view2 as v2 left join view1 as v1
on v1.town=v2.town
You may be after a full outer join
select
houses.town,
flats.flats,
houses.houses,
houses.BrickWall,
houses.TileRoofs
from flats
full outer join houses
on houses.town=flats.town
and houses.TileRoofs = flats.TileRoofs
and houses.BrickWall = flats.BrickWall

Getting normalized values from two foreign key fields, SQL, double join?

An example is the easiest way to explain what I'm looking to do:
GIVEN:
~move~
id | from | to
--------------
1 | 1 | 2
2 | 1 | 2
3 | 2 | 3
4 | 3 | 1
~locations~
id | name
---------
1 | home
2 | work
3 | out
How can I get:
id | from | to
----------------
1 | home | work
2 | home | work
3 | work | out
4 | out | home
That is, the human-readable name for both the from and to columns.
Select
Move.ID,
[From] = FromLocation.Name,
[To] = ToLocation.Name
From
Move
Inner Join Location As FromLocation On Move.[From] = FromLocation.ID
Inner Join Location As ToLocation On Move.[To] = ToLocation.ID