PostgreSQL multiple join - sql

I would like to make multiple joins in one query, I have four tables for that with the following structure:
Table 1 fields, t1: did (int), finished (datetime), userid (int)
Table 2 fields, t2: userid (int), name
Table 3 fields, t3: blid (int), did (int), count (int), btid(int)
Table 4 fields, t4: btid(int), denom (int)
I want to make a query where I would get all fields of t1 except for that one joined with t2 in that case I would like to show t2.name when t1.userid = t2.userid This is fair simple:
select t1.did, t1.finished, t2.name, t2.userid from t1 inner join on t1.userid=t2.userid
But I also want to add a column composed by the following query:
select sum(t3.count*t4.denom) from t3 inner join t4 on t3.btid=t4.btid
Only when t3.did=t1.did I don't know if it would be an outer join or otherwise, but I want to add the previous query as a column in the first query and it would be something like:
t1.did, t1.finished, t2.name, t2.userid, "total"?
The real-world problem is that users make money deposits t1 users information are held in t2. The amount deposited is described in t3 for every deposit t1.did and related denominations of each deposit breakdown, in t3 are described in t4.

You might find this easiest as a correlated subquery:
select t1.did, t1.finished, t2.name, t2.userid,
(select sum(t3.count * t4.denom)
from t3 inner join
t4
on t3.btid = t4.btid
where t3.did = t1.did
) as value
from t1 inner join t2
on t1.userid = t2.userid;
For this version, you want indexes on t3(did, btid, count) and t4(btid, denom).
You can also phrase this as a join and group by:
select t1.did, t1.finished, t2.name, t2.userid,
sum(t3.count * t4.denom)
from t1 inner join t2
on t1.userid = t2.userid left join
t3
on t3.did = t1.did left join
t4
on t3.btid = t4.btid
group by t1.did, t1.finished, t2.name, t2.userid;

You can use group by clause:
select t1.did, t1.finished, t2.name, t2.userid, sum(t3.count*t4.denom)
from t1
inner join t2 on t1.userid=t2.userid
inner join t3 on t1.did = t3.did
inner join t4 on t3.btid = t4.btid
group by t1.did, t1.finished, t2.name, t2.userid

Related

Sum on Multiple Left Joins

I am trying to sum the total value of a column for a specific ID after multiple left joins.
The below code gives me what I am looking for but across multiple rows, I need the value for T3.C_Amt and T4.E_Amt to be totaled.
SELECT
T1.ID,
T2.Unique_ID,
T3.C_Date,
T3.C_Amount,
T4.D_Date,
T4.D_Amount
FROM
TABLE_1 T1
LEFT JOIN DATABASE1.TABLE_2 T2
ON T1.ID = T2.UNIQUE_ID
LEFT JOIN DATABASE1.TABLE_3 T3
ON T2.Unique_ID = T3.Unique_ID
AND T3.C_Date = '2019-04-11'
LEFT JOIN DATABASE1.TABLE_4 T4
ON T2.Unique_ID = T4.Unique_ID
AND T4.D_Date= '2019-04-11'
--this needs to be summed to have the total amount
I want it to return one row for the Unique ID with total C_Amount and total D_Amount for the specific date
Use aggregation with group by
SELECT T2.Unique_ID,T3.C_Date,sum(T3.C_Amount),
T4.D_Date,sum(T4.D_Amount)
FROM TABLE_1 T1
LEFT JOIN DATABASE1.TABLE_2 T2
ON T1.ID = T2.UNIQUE_ID
LEFT JOIN DATABASE1.TABLE_3 T3
ON T2.Unique_ID = T3.Unique_ID AND T3.C_Date = '2019-04-11'
LEFT JOIN DATABASE1.TABLE_4 T4
ON T2.Unique_ID = T4.Unique_ID AND T4.D_Date= '2019-04-11'
group by T2.Unique_ID,T3.C_Date,T4.D_Date
I would do it this way. Since Teradata is a MPP, there should not be much of a performance impact as well.
SELECT Unique_ID,C_Date,sum(C_Amount),D_Date,sum(D_Amount)
FROM
(
SELECT
T1.ID ID,
T2.Unique_ID Unique_ID,
T3.C_Date C_Date,
T3.C_Amount C_Amount,
T4.D_Date D_Date,
T4.D_Amount D_Amount
FROM
TABLE_1 T1
LEFT JOIN DATABASE1.TABLE_2 T2
ON T1.ID = T2.UNIQUE_ID
LEFT JOIN DATABASE1.TABLE_3 T3
ON T2.Unique_ID = T3.Unique_ID
AND T3.C_Date = '2019-04-11'
LEFT JOIN DATABASE1.TABLE_4 T4
ON T2.Unique_ID = T4.Unique_ID
AND T4.D_Date= '2019-04-11'
) ABC
GROUP BY Unique_ID,C_Date,D_Date
I would add a concern of 1-to-many causing a false total. What if table 3 has 10 records for a given T2.UniqueID and another 5 for the T4 table. You have just compounded your total completely out of range.
As such, I would pre-aggregate from the child tables grouped by the unique ID filtered on the date. Also, you can remove the T2 table due to associative properties.
T1.ID = T2.Unique_ID = T3.Unique_ID = T4.Unique_ID
to T1.ID = T3.Unique_ID = T4.Unique_ID
SELECT
T1.ID,
T3.C_Date,
T3.C_Amount,
T4.D_Date,
T4.D_Amount
FROM
TABLE_1 T1
LEFT JOIN
( Select Unique_ID, sum( C_Amount ) as T3Sum
from DATABASE1.TABLE_3
where T3.C_Date = '2019-04-11'
group by Unique_ID ) T3
ON T1.ID = T3.Unique_ID
LEFT JOIN
( select Unique_ID, sum( D_Amount ) T4Sum
from DATABASE1.TABLE_4
where D_Date= '2019-04-11'
group by Unique_ID ) T4
ON T1.ID = T4.Unique_ID
Now, your ambiguity on table names might help being more real-life descriptive. Your summary amounts are based on a single date, but how many records in T1 that are applicable? If you have 5k rows in T1 and only 450 entries total between tables T3 and T4, your result set would still give you all the rows. That being said, you probably dont want the bloat of records that don't have any such details in the secondary sum subqueries. I would add a WHERE clause at the end
WHERE
T3.Unique_ID IS NOT NULL
OR T4.Unique_ID IS NOT NULL

Joining tbl1 to select statement twice with join to tbl2 that also joins to tbl3

I'm using SQL server manger.
I have 3 tables
I need a query that pulls t1 ands add an Origin Basin and a Destination Basin.
So far I have the following:
select T1.[Country (destination)], T3.AreaName
From T1
left outer join T2 on
T1.[Country (destination)] = T2.CountryName
inner join T3 on
T2.AreaID = T3.AreaID
inner join T3 on
T2.AreaID = T3.AreaID
Which returns:
Country | Area
However, I'm having trouble doing this for the second country column. I believe you use aliases. I've tried:
select (select AreaName
FROM T3
where T3.AreaID = T2.AreaID) as 'Area Imp',
(select AreaID
From T2
where T2.CountryName = T1.[Country (origin)]) as 'x',
(select AreaID
From T2
where T2.CountryName = T1.[Country (destination)]) as 'y'
FROM T1
But I can't get it to work.
This is what you need to do:
select t1.date, t1.country_destination, t1.country_origin, destination_area.AreaName as area_destination, origin_area.AreaName as area_origin
from t1 as t1 join t2 as destination on t1.country_destination = destination.countryname
join t2 as origin on t1.country_origin = origin.countryname
join t3 as destination_area on t2.areaid = destination_area.areaid
join t3 as origin_area on t2.areaid = origin_area.areaid
You will need to join with the same table twice, both for t2 and t3 so that you get the matching records for your needs.
It helps usually to put aliases that match the purpose of the join (in this case, destination and origin) when writing the query.
I think what you're trying to do is something like this:
select T1.*, T3dest.AreaName, T3orig.AreaName
From
T1
inner join
T2 T2dest on
T1.[Country (destination)] = T2dest.CountryName
inner join
T3 T3dest on
T2dest.AreaID = T3dest.AreaID
inner join
T2 T2orig on
T1.[Country (origin)] = T2orig.CountryName
inner join
T3 T3orig on
T2orig.AreaID = T3orig.AreaID
Note that I've switched to inner joins throughout, at the moment. If you do want left join semantics, you either need to use those for all of the joins to the T2 and T3 tables or you need to change the join order (so that the relevant T3 joins to the T2 tables occur before the attempted join with T1). It's not clear from the sample data if that's required, however.
Try this, You would still want to join on area id's
select T1.Date,T1.[Country (destination)], null [Country (origin)], T3.AreaName [AreaName(Destination)], null [AreaName(Origin)]
From T1
left outer join T2 on
T1.[Country (destination)] = T2.CountryName
inner join T3 on
T2.AreaID = T3.AreaID
union all
select T1.Date,null [Country (destination)], t1.[Country (origin)], Null [AreaName(Destination)], t3. [AreaName(Origin)]
From T1
left outer join T2 on
T1.[Country (Origin)] = T2.CountryName
inner join T3 on
T2.AreaID = T3.AreaID

How do I get the results of one JOIN and THEN feed those into a separate join in T-SQL?

I'm trying to JOIN 2 tables ON a key like
SELECT column1,column2
FROM Table1 t1
INNER JOIN Table2 t2 ON t1.t2id = t2.id
Now, I have a 3rd table that has a Foreign Key with t2's id that I want to join... When I do
LEFT JOIN
Table3 t3 ON t3.t2id = --<-------------- This is where I'm lost
I don't know if I should do ON t3.t2id = t1.t2id OR ON t3.t2id = t2.id
What I need is the list of t2ids which are still in the picture after the first join. However, it seems as though if I specify either of the above, it will just pull ids from the original table before the first join?
To clarify one more time: I'm trying to essentially do a INNER JOIN of Table1 and Table2, get the resulting table, then get the t2ids of those results and feed them into a final join such that the final result contains all of Table3's rows as well as the data from the first join
You said: "final result contains all of Table3's rows as well as the data from the first join".
It means that you need
Table3 LEFT JOIN <previous results>
instead of
<previous results> LEFT JOIN Table3
The easiest way to write it is to use Common-Table Expressions:
WITH
CTE_InnerJoin
AS
(
SELECT column1, column2, t1.t2id
FROM
Table1 t1
INNER JOIN Table2 t2 ON t1.t2id = t2.id
)
SELECT
CTE_InnerJoin.column1
,CTE_InnerJoin.column2
,Table3....
FROM
Table3
LEFT JOIN CTE_InnerJoin ON CTE_InnerJoin.t2id = Table3.t2id
;
It doesn't matter what column you include in the CTE: t1.t2id or t2.id, the values in them are the same, because they are inner-joined together.
JOINs already do exactly what you want. A JOIN isn't always between two tables. Frequently, it's between the results of previous joins.
SELECT column1,column2
FROM Table1 t1
INNER JOIN Table2 t2 ON t1.t2id = t2.id
LEFT JOIN
Table3 t3 ON t3.t2id = t2.id
At the point at which you're writing the final ON clause here, what you're joining is precisely the results of the previous INNER JOIN on the left and the table Table3 on the right. All of t1, t2 and t3 are in scope within the ON clause, but note that t1 and t2 are now both used as aliases into the same source of rows - the previous INNER JOIN.
As a further example, consider the "diamond join":
SELECT
*
FROM
t1
left join
t2
on
t1.a = t2.b
left join
t3
on
t1.c = t3.d
inner join
t4
on
t2.e = t4.f OR
t3.g = t4.h
This is a way of joining two tables (t1 and t4) based on two alternative joins. Note that in the final inner join, what is on the "left" is the result of already joining tables t1, t2 and t3.
Join the table having foreign key with
Try this....
SELECT column1,column2
FROM Table1 t1
INNER JOIN Table2 t2 ON t1.t2id = t2.id
LEFT join Table3 t3 ON t2.id=t3.t2id
Or Like this.
SELECT t12.column1 ,
t12.column2 ,
t3.*
FROM (
--- INNER JOIN of Table1 and Table2, get the resulting table,
SELECT t1.column1 ,
t2.column2 ,
t1.t2id --- or t2.id doesn't matter because its inner join
FROM Table1 t1
INNER JOIN Table2 t2 ON t1.t2id = t2.id
) T12
LEFT JOIN Table3 T3 ON t3.t2id = t1.t2id --- then get the t2ids of those results
--- and feed them into a final join
--- if you want to get all rows from Table3, Change LEFT JOIN Table3 T3 ON t3.t2id = T1.t2id
--- into RIGHT JOIN Table3 T3 ON t3.t2id = T1.t2id
try this
select t3.*, column1, column2
from
table1 t1 inner join table2 t2 on t1.t2id = t2.id
right outer join table3 t3 on t3.t2id = t2.id
equvalent to
select t3.*, column1, column2
from
table1 t1 inner join table2 t2 on t1.t2id = t2.id
right outer join table3 t3 on t3.t2id = t1.t2id
if you want all rows from table 3 and those matching rows from table1 inner joined to table2 then you can use this syntax:
select t3.*,
column1, column2
from table3 t3
left join table2 t2
inner join table1 t1
on t1.t2id = t2.id
on t3.t2id = t2.id

Convert to join query

select t.* from table1 t where t.id NOT IN(
select Id from t2 where usrId in
(select usrId from t3 where sId=value));
I the result i need is like if there are matching id's in t1 and t2 then those id's should be omitted and only the remaining rows should be given to me. I tried converting into join but it is giving me the result i wanted. Below is my join query.
SELECT t.* FROM table1 t JOIN table2 t2 ON t.Id <> t2.Id
JOIN table3 t3 ON t3.Id=t2.Id WHERE t3.sId= :value
This doesn't feth me the correct result. it was returning all the rows, but i want to restrict the result based on the matching id's in table t1 and table t2. Matching id's should be ommited from the result.I will be passing the value for sId.
I believe this to be an accurate refactor of your query using joins. I don't know if we can do away with the subquery, but in any case the logic appears to be the same.
select t1.*
from table1 t1
left join
(
select t2.Id
from table2 t2
inner join table3 t3
on t2.usrId = t3.usrId
where t3.sId = <value>
) t2
on t1.Id = t2.Id
where t2.Id is null
Let's break down and solve problem step by step.
So your query
select t.* from table1 t where t.id NOT IN(
select Id from t2 where usrId in
(select usrId from t3 where sId=value));
on converting the inner query to JOIN will yield
select t.* from table1 t where t.id NOT IN
(SELECT T2.ID FROM T2 JOIN T3 on T2.UsrID =T3.UsrID and T3.sID=value)
which on further converting to JOIN with outer table will be
select t.* from table1 t LEFT JOIN
(SELECT T2.ID FROM T2 JOIN T3 on T2.UsrID =T3.UsrID and T3.sID=value)t4
ON t.id =T4.ID
WHERE t4.ID is NULL
In case you completely want to remove sub-query you can try like this
SELECT t.*
FROM table1 t
LEFT JOIN T2
ON T.ID=T2.ID
LEFT JOIN T3
ON T3.UsrId=T2.UsrID AND T3.sId=value
WHERE T3.UsrID IS NULL

SQL Joins using NULL

I want to join the two tables above to show the appropriate Authorizationcode based on the NameID.
If the NameID matches, then will show corresponding Authorizationcode,
If it does not, then it will show the other Authorizationcode
NameID and OrderID are uniqueidentifiers.
The result should be:
cyclophosphamide - Auth01234
Adriamycin RDF - Auth01234
Neulasta - Auth04567
Not able to join based on NULL NameID. Please suggest.
This is a bit tricky. Here is a method that uses two left joins. The condition on the second one only chooses the NULL row from table2 (a cross join could also be used):
select t1.name, coalesce(t2.authorizationcode, t2null.authorizationcode) as authorizationcode
from table1 t1 left join
table2 t2
on t1.nameid = t2.nameid left join
table2 t2null
on t2null.nameid is null;
This is ANSI standard and should work in most databases.
I would approach this with a UNION
SELECT DISTINCT NameID, AuthorisationCode
FROM
( SELECT NameID, AutorisationCode
FROM table1 t1 INNER JOIN
table2 t2 ON t1.nameid = t2.nameID
UNION
SELECT NameID, AutorisationCode
FROM table1 t1 LEFT OUTER JOIN
table2 t2 ON t1.nameid = t2.nameID
) DATA
Try this one:
SELECT t1.Name, ISNULL(t2.AuthorizationCode, t3.AuthorizationCode) AS AuthorizationCode
FROM Table1 AS t1
LEFT JOIN Table2 AS t2 on t1.NameID = t2.NameID AND t1.OrderID = t2.OrderID
LEFT JOIN Table2 AS t3 on t3.NameID IS NULL AND t1.OrderID = t3.OrderID
I assume you also want to join on OrderID. If not please make your question more clear.
Your screenshots are from management studio so a SQL Server specific answer.
SELECT t2.Name, CA.AuthorizationCode
FROM Table2 t2
CROSS APPLY
(
SELECT TOP (1) *
FROM Table1 t1
WHERE t1.OrderId = t2.OrderId
AND (t1.NameId = t2.NameId OR t1.NameId IS NULL)
ORDER BY t1.NameId DESC -- not null prioritised over null
) CA
It would need an index on table1 (OrderId, NameId)