SQL: Multiple joins on multiple tables where some column values are not mandatory - sql

I am trying to join multiple tables where the values in columns e.Email, e.Phone are voluntary. I would like to select all rows even if e.Email, e.Phone contain empty values.
Currently, I am only able to select rows where e.Email, e.Phone values are present.
SELECT a.ID_1
,b.Tier
,e.Email
,e.Phone
FROM CustomerActivity a
JOIN CustomerSummary b
ON a.ID_1 = b.ID_1
JOIN DimensionCustomer c
ON b.ID_1 = c.ID_1
JOIN LegacyCustomerMapping d
ON c.ID_2 = d.ID_2
JOIN LegacyCustomerContactData e
d.ID_3 = e.ID_3
Many thanks for any kind of advice!

I would recommend using left join instead of join in your query. That should get the results you are looking for.
Image from: https://www.w3schools.com/sql/sql_join.asp

This can be obtained by using a LEFT OUTER JOIN for the table containing the Email and Phone fields:
SELECT a.ID_1, b.Tier, e.Email ,e.Phone
FROM CustomerActivity a
JOIN CustomerSummary b ON a.ID_1 = b.ID_1
JOIN DimensionCustomer c ON b.ID_1 = c.ID_1
JOIN LegacyCustomerMapping d ON c.ID_2 = d.ID_2
LEFT OUTER JOIN LegacyCustomerContactData e d.ID_3 = e.ID_3
Using an OUTER JOIN avoids records not being returned when there are no related rows in the joined table. Note that OUTER is optional and might not be supported by all databases.

Related

Unnecessary LEFT JOINs on all Child tables from main LEFT JOIN table required in query for correct result

I am LEFT JOIN-ing the table RECALLS_T with EVENTS_T. Some Recalls do not have any Events, and for those I want a blank row returned.
However, once an entry in EVENTS_T exists, all the following extra tables from EVENTS_T which I also need (ANSWERS_T, ACTIVITY_QUESTIONS_T, ACTIVITES_T) are guaranteed to have entries. For these subsequent tables there is no need to do a LEFT JOIN from their parent, it can be an INNER JOIN just as well.
But if I do the following, the query does not return blank EVENTS_T rows for a RECALLS_T.
SELECT ..
FROM
recalls_t r
LEFT JOIN events_t e ON ( r.id = e.recall_id ) --Only LEFT JOIN this main table
INNER JOIN answers_t ans ON (ans.event_id = e.id)
INNER JOIN activity_questions_t aq ON (ans.activity_question_id = aq.id)
INNER JOIN PUBLIC.activities_t act ON (aq.activity_id = act.id)
Instead, I need to change every subsequent INNER JOIN to a LEFT JOIN as well, in order to get Recalls with blank Events.
SELECT ..
FROM
recalls_t r
LEFT JOIN events_t e ON ( r.id = e.recall_id )
LEFT JOIN answers_t ans ON (ans.event_id = e.id)
LEFT JOIN activity_questions_t aq ON (ans.activity_question_id = aq.id)
LEFT JOIN PUBLIC.activities_t act ON (aq.activity_id = act.id)
I'm just not clear on why I have to do this. The top-level optional join is all I care about, but the downstream joins from EVENTS_T are guaranteed to have data provided the Event exists. Shouldn't the top-level single EVENTS_T join be enough? I'm using Postgres.
The reason you need to do this is because rows preserved by the outer join will have NULL for all columns from events - so inner joining using an equality condition on e.id will remove them from the result again.
you can move the ON clause to the end of the query to give your desired semantics.
The left join is then on the virtual result of inner joining events_t, answers_t, activity_questions_t and activities_t
SELECT ...
FROM recalls_t r
LEFT JOIN events_t e
INNER JOIN answers_t ans
ON ( ans.event_id = e.id )
INNER JOIN activity_questions_t aq
ON ( ans.activity_question_id = aq.id )
INNER JOIN PUBLIC.activities_t act
ON ( aq.activity_id = act.id )
ON ( r.id = e.recall_id )
Or you could consider a RIGHT JOIN here instead
SELECT ...
FROM events_t e
INNER JOIN answers_t ans
ON ( ans.event_id = e.id )
INNER JOIN activity_questions_t aq
ON ( ans.activity_question_id = aq.id )
INNER JOIN PUBLIC.activities_t act
ON ( aq.activity_id = act.id )
RIGHT JOIN recalls_t r
ON ( r.id = e.recall_id )
You have to do this because answers_t`` is joining onevents_s. If thejoinkey (e.recall_id) isNULL`, then there will be no match. And the inner join will not return the row. And so on for the other tables.
You seem to understand the fix. Once you use left join, you need to continue using left join for those tables that are connected to the second table of the left join.

Need to join but on a secondary table

I have this one view all_people_expanded_view that has all the data needed except 1.
The race of client is in race table. But the description is in race_info. I need to join on race on people_id, but really need then to join race on race_info on column race_info_id and then get description. I am stuck on how to make this connection.
select a.full_name, a.dob, a.gender, a.ethnicity, c.race_info_id
from all_people_expanded_view a inner join
race c
on a.people_id = c.people_id
this would be fine but it only has race_info_id and not the description which is in the race_info_id table.
Is this what you're looking for?
select
a.full_name,
a.dob,
a.gender,
a.ethnicity,
c.race_info_id,
ri.description
from
all_people_expanded_view a
inner join
race c
on a.people_id = c.people_id
left join --Maybe an inner join, depending on your data
race_info ri
on ri.race_info_id = c.race_info_id
select
a.full_name, a.dob, a.gender, a.ethnicity, c.race_info_id , ri.description
from
all_people_expanded_view a
inner join race c on a.people_id = c.people_id
inner join race_info ri on ri.race_info_id = c.race_info_id

How to Fix Right Outer Join

I am Trying to make Left Outer Join Use bellow code;
The Result Using (LEFT OUTER , RIGHT OUTER, FULL OUTER And INNER JOIN) is The same Result.
I am Try to return All Publisher Name Include Those who don't connected with any Books yet!
SELECT P.PublisherName,
COUNT(B.BookID) AS BookPublished
FROM LR_Publisher AS P LEFT OUTER JOIN LR_Book As B
ON P.PublisherID = B.PublisherID
WHERE (P.PublisherID = #pPublisherID OR #pPublisherID IS NULL)
GROUP BY PublisherName
Thanks In Advance
Publishers that have no books published are filtered out by GROUP BY.
It can be fixed by using GROUP BY ALL:
SELECT P.PublisherName,
COUNT(B.BookID) AS BookPublished
FROM LR_Publisher AS P
LEFT OUTER JOIN LR_Book As B ON P.PublisherID = B.PublisherID
-- WHERE (P.PublisherID = #pPublisherID OR #pPublisherID IS NULL)
GROUP BY PublisherName
you could write it this way
select P.PublisherName,
isnull(bi.BookPublished, 0) as BookPublished
from LR_Publisher as P
left join (
select B.PublisherID, Count(B.BookID) BookPublished
from LR_Book as B
where (#pPublisherID is null or B.PublisherID = #pPublisherID)
group by B.PublisherID
) bi on P.PublisherID = Bi.PublisherID
where (#pPublisherID is null or P.PublisherID = #pPublisherID)
I also was not aware of the filtering done by group by. Thank you for the question.
simple left join will help you want all publisher name including who does not published any book yet
SELECT P.PublisherName
FROM LR_Publisher AS P LEFT OUTER JOIN LR_Book As B
ON P.PublisherID = B.PublisherID

List all donations made, by both individual alumni and business donors. Name, ID of the donor, date and amount of the donation must be displayed

Need help in this problem. I have the following tables but i cannot seem to get any data out based on the description and query as below.
Corporate(CorporateID(PK), CorporateName, CorporateAddress)
Donation(DonationID(PK), TypeOfDonations)
Alumnus(AlumnusID(PK), CityPK(FK), AlumnusName, EmailAddress, WorkPhoneNumber, HomePhoneNumber, Address
Donation_Made(CorporateDonationID(PK), DonationID(FK), CorporateID(FK), AlumnusID(FK), DonationAmount, DateOfDonation
SELECT Z.DONATIONID, A.ALUMNUSNAME, C.CORPORATENAME, Z.DATEOFDONATION, Z.DONATIONAMOUNT
FROM ALUMNUS A,
(SELECT * FROM DONATION D LEFT JOIN DONATION_MADE DM
ON D.DONATIONID = DM.DONATIONID)Z LEFT JOIN CORPORATE C
ON C.CORPORATEID = DM.CORPORATEID AND A.ALUMNUSID=DM.ALUMNUSID AND Z.TYPEOFDONATIONS= 'MONETARY';
You should start with the fact table, i.e. DONATION_MADE, and then (outer) join the related tables:
SELECT DM.DONATIONID,
A.ALUMNUSNAME,
C.CORPORATENAME,
DM.DATEOFDONATION,
DM.DONATIONAMOUNT
FROM DONATION_MADE DM
INNER JOIN DONATION D
ON D.DONATIONID = DM.DONATIONID
LEFT JOIN ALUMNUS A,
ON A.ALUMNUSID = DM.ALUMNUSID
LEFT JOIN CORPORATE C
ON C.CORPORATEID = DM.CORPORATEID
WHERE D.TYPEOFDONATIONS = 'MONETARY';
You need to use outer join to get data from both Corporate and Alumnus tables.
select a.AlumnusID, a.AlumnusName, c.CorporateID, c.CorporateName, dm.DateOfDonation, dm.DonationAmount
from Donation_Made dm
left outer join on Alumnus a on dm.AlumnusID = a.AlumnusID
left outer join on Corporate c on dm.CorporateID = c.CorporateID
inner join on Donation d on dm.DonationID = d.DonationID
where d.TypeOfDonations='MONETARY';

Combining two independent inner joins into one on certain conditions

I have the below two queries that are shown below, actually both of the queries use an inner join internally. Now I want to combine these two queries into one, but the condition that where the value of column invo_id = 2 and the column invo_id is in table ioa_invoice_line
So the relationship among three tables
ioa_invoice_line
ioa_invoice_line_notes
IOA_INV_LINE_NOTE_MAP
These are the queries
select
e.id
, e.invo_id
, b.id
, b.inli_id
from
ioa_invoice_line e
inner join
ioa_invoice_line_notes b on e.id = b.inli_id;
select
e.id
, b.id
, b.ATTACHMENT_BLOB_ID
, b.INV_LINE_NOTE_ID
from
ioa_invoice_line_notes e
inner join
IOA_INV_LINE_NOTE_MAP b on e.id = b.INV_LINE_NOTE_ID;
ioa_invoice_line enter image description here
This is the way I assumed you'd want to do the joins. Since you're looking for two conditions to be true:
1) That ioa_invoice_line.invo_id = 2
2) That ioa_invoice_line.invo_id exists
Since you'll be checking to see that invo_id is equal to 2 you can be pretty sure that it exists if it finds a value of 2.
SELECT L.id, L.invo_id, LN.id, LNM.id, LNM.attachment_blob_id, LNM.inv_line_note_id
FROM ioa_invoice_line_notes LN
INNER JOIN ioa_invoice_line L ON LN.inli_id = L.id
INNER JOIN ioa_inv_line_note_map LNM ON LN.id = LNM.inv_line_note_id
WHERE L.invo_id = 2;
It just looked like you were looking for Inv Line ID, Inv Line Invoice ID, Line Notes ID, Line Notes Map ID and the other IDs in the Notes Map table.
The query integrates the condition on the invo_id into the join. As it is an inner join, the existence of invo_id in the selected records comes for free:
select line.id id_line
, line.invo_id
, notes.id id_notes
, notes.inli_id
, map.id id_map
, map.attachment_blob_id
, map.inv_line_note_id
from ioa_invoice_line line
INNER JOIN ioa_invoice_line_notes notes on (
line.id = notes.inli_id
AND line.invo_id = 2
)
INNER JOIN ioa_inv_line_note_map map on map.id = notes.inv_line_note_id
;