Inner join of 3 tables with row count - sql

I have 3 tables A,B and C as shown below.
All the 3 tables have a common CUST_ID column. And table A and B have 2 common columns CUST_ID and A_ID. I have to display the NAME from table C, Count of Test_ID from table B for each CUST_ID and START and END from table A. The expected result table is like,
I tried inner join like,
select C.Name ,A.Start,A.[End]
from
C
inner join
B
on C.CUST_ID = B.CUST_ID
inner join
A
on A.A_ID = B.A_ID and A.CUST_ID = B.CUST_ID
but its giving total 7 rows. How to display the count of Test_ID for east CUST_ID and display it in only 4 rows as shown in the expected result table?

Try this
SELECT C.NAME, Tmp.Test_COUNT, A.START, A.END
FROM C INNER JOIN A ON C.CUST_ID = A.CUST_ID
INNER JOIN
(
SELECT CUST_ID, A_ID, COUNT(*) AS Test_COUNT
FROM B
GROUP BY CUST_ID, A_ID
) Tmp ON Tmp.CUST_ID = A.CUST_ID AND Tmp.A_ID = A.A_ID

You need to use a subquery to obtain the test counts in table B, and join this table instead of joining B directly:
SELECT C.NAME, t.Test_COUNT, A.START, A.END
FROM C
INNER JOIN A
ON C.CUST_ID = A.CUST_ID
INNER JOIN
(
SELECT A_ID, COUNT(*) AS Test_COUNT
FROM B
GROUP BY A_ID
) t
ON t.A_ID = A.A_ID

Related

Conditional join in SQL Server dependent on other table values

I need make a decision which table should be use in join statement depend on values in another table
I tried using CASE and COALESCE but can't achieve any success.
TableA has A and B and C and many other columns
TableB has ID and NAME columns
TableC has ID and NAME columns
My select statement is;
Select A.D, A.E, A.F From TableA A
If A.E = 1 then the following join should be used
left outer join TableB B ON A.B = B.ID
and B.NAME should be returned in the select statement
If A.E = 2 then the following join should be used
left outer join TableC C ON A.B = C.ID
and C.NAME should be returned in the select statement
Just add your conditions to the joins, and then use a case statement to pull the correct field to your result set e.g.
select A.D, A.E, A.F
, case when B.[Name] is not null then B.[Name] else C.[Name] end [Name]
from TableA A
left outer join TableB B ON A.B = B.ID and A.E = 1
left outer join TableC C ON A.B = C.ID and A.E = 2
Join tablea with the union of tableb with an extra column with value 1 and tablec with an extra column with value 2 and apply the conditions in the ON clause:
select
a.D, a.E, a.F, u.NAME
from tablea a
left join (
select *, 1 col from tableb
union all
select *, 2 col from tablec
) u on a.B = u.id and a.E = u.col

SQL, get just recent ( in one row) from many to many relation ship

I have 3 tables like
A. (Aid,person)
B. (Bid,event,InsertDate)
C. (Cid,Aid,Bid)
now I need to get last recent event base on B.InsertDate desc from joined B.event and A.Person (last event of each person)
I tried join but that make multiple A.Person and B.event.
Can you guide me please?
Update:
for now I just add another lastUpdate column to person table and update that for each insert in Event table and made that equal to InsertDate. so my query is like:
SELECT
A.person, B.event
from tableA A
join tableC C
on A.Aid = C.Aid
join tableB B
on B.Bid = C.Bid and B.InsertDate = A.lastUpdate
Try this:
SELECT
A.person,
B.event,
MAX(B.InsertDate)
FROM
A
JOIN C ON A.Aid = C.Aid
JOIN B ON B.Bid = C.Bid
GROUP BY
A.person,
B.event
May be something lyk this
WITH cte
AS (SELECT Row_number() OVER (partition BY person ORDER BY B.InsertDate DESC) rn,
A.person,
B.event,
B.InsertDate AS LastEventDate
FROM B
JOIN (SELECT B.event,
Max(B.InsertDate) InsertDate
FROM B
GROUP BY B.event) sub
ON sub.event = B.event
AND sub.InsertDate = b.InsertDate
JOIN A
ON A.Bid = C.Bid
JOIN C
ON B.Bid = C.Bid)
SELECT *
FROM cte
WHERE rn = 1

SQL summations with multiple outer joins

I have tables a, b, c, and d whereby:
There are 0 or more b rows for each a row
There are 0 or more c rows for each a row
There are 0 or more d rows for each a row
If I try a query like the following:
SELECT a.id, SUM(b.debit), SUM(c.credit), SUM(d.other)
FROM a
LEFT JOIN b on a.id = b.a_id
LEFT JOIN c on a.id = c.a_id
LEFT JOIN d on a.id = d.a_id
GROUP BY a.id
I notice that I have created a cartesian product and therefore my sums are incorrect (much too large).
I see that there are other SO questions and answers, however I'm still not grasping how I can accomplish what I want to do in a single query. Is it possible in SQL to write a query which aggregates all of the following data:
SELECT a.id, SUM(b.debit)
FROM a
LEFT JOIN b on a.id = b.a_id
GROUP BY a.id
SELECT a.id, SUM(c.credit)
FROM a
LEFT JOIN c on a.id = c.a_id
GROUP BY a.id
SELECT a.id, SUM(d.other)
FROM a
LEFT JOIN d on a.id = d.a_id
GROUP BY a.id
in a single query?
Your analysis is correct. Unrelated JOIN create cartesian products.
You have to do the sums separately and then do a final addition. This is doable in one query and you have several options for that:
Sub-requests in your SELECT: SELECT a.id, (SELECT SUM(b.debit) FROM b WHERE b.a_id = a.id) + ...
CROSS APPLY with a similar query as the first bullet then SELECT a.id, b_sum + c_sum + d_sum
UNION ALL as you suggested with an outer SUM and GROUP BY on top of that.
LEFT JOIN to similar subqueries as above.
And probably more... The performance of the various solutions might be slightly different depending on how many rows in A you want to select.
SELECT a.ID, debit, credit, other
FROM a
LEFT JOIN (SELECT a_id, SUM(b.debit) as debit
FROM b
GROUP BY a_id) b ON a.ID = b.a_id
LEFT JOIN (SELECT a_id, SUM(b.credit) as credit
FROM c
GROUP BY a_id) c ON a.ID = c.a_id
LEFT JOIN (SELECT a_id, SUM(b.other) as other
FROM d
GROUP BY a_id) d ON a.ID = d.a_id
Can also be done with correlated subqueries:
SELECT a.id
, (SELECT SUM(debit) FROM b WHERE a.id = b.a_id)
, (SELECT SUM(credit) FROM c WHERE a.id = c.a_id)
, (SELECT SUM(other) FROM d WHERE a.id = d.a_id)
FROM a

Left Join Multiple Tables and Avoid Duplicates

I have two tables with a 1:n relationship to my base table, both of which I want to LEFT JOIN.
-------------------------------
Table A Table B Table C
-------------------------------
|ID|DATA| |ID|DATA| |ID|DATA|
-------------------------------
1 A1 1 B1 1 C1
- - 1 C2
I'm using:
SELECT * FROM TableA a
LEFT JOIN TableB b
ON a.Id = b.Id
LEFT JOIN TableC c
ON a.Id = c.Id
But this is showing duplicates for TableB:
1 A1 B1 C1
1 A1 B1 C2
How can I write this join to ignore the duplicates? Such as:
1 A1 B1 C1
1 A1 null C2
I think you need to do logic to get what you want. You want for any multiple b.ids to eliminate them. You can identify them using row_number() and then use case logic to make subsequent values NULL:
select a.id, a.val,
(case when row_number() over (partition by b.id, b.seqnum order by b.id) = 1 then val
end) as bval
c.val as cval
from TableA a left join
(select b.*, row_number() over (partition by b.id order by b.id) as seqnum
from tableB b
) b
on a.id = b.id left join
tableC c
on a.id = c.id
I don't think you want a full join between B and C, because you will get multiple rows. If B has 2 rows for an id and C has 3, then you will get 6. I suspect that you just want 3. To achieve this, you want to do something like:
select *
from (select b.*, row_number() over (partition by b.id order by b.id) as seqnum
from TableB b
) b
on a.id = b.id full outer join
(select c.*, row_number() over (partition by c.id order by c.id) as seqnum
from TableC c
) c
on b.id = c.id and
b.seqnum = c.seqnum join
TableA a
on a.id = b.id and a.id = c.id
This is enumerating the "B" and "C" lists, and then joining them by position on the list. It uses a full outer join to get the full length of the longer list.
The last join references both tables so TableA can be used as a filter. Extra ids in B and C won't appear in the results.
Do you want to use distinct
SELECT distinct * FROM TableA a
LEFT JOIN TableB b
ON a.Id = b.Id
LEFT JOIN TableC c
ON a.Id = c.Id
Do it as a UNION, i.e.
SELECT TableA.ID, TableB.ID, TableC.Id
FROM TableA a
INNER JOIN TableB b ON a.Id = b.Id
LEFT JOIN TableC c ON a.Id = c.Id
UNION
SELECT TableA.ID, Null, TableC.Id
FROM TableA a
LEFT JOIN TableC c ON a.Id = c.Id
i.e. one SELECT to being back the first row and another to bring back the second row. It's a bit rough because I don't know anything about the data you are trying to read but the principle is sound. You may need to rework it a bit.

How many B and C has A?

I have this tables:
A:
id
1
2
B:
id a_id
1 1
2 1
3 1
C:
id a_id
1 1
2 1
3 2
I need this result:
A, CountB, CountC
1, 3, 2
2, 0, 1
This try doesnt work fine:
SELECT
A.id, COUNT(B.id), COUNT(C.id)
FROM
A
LEFT JOIN
B ON A.id = B.a_id
LEFT JOIN
C ON A.id = C.a_id
GROUP BY A.id
How must be the sql sentence without using correlative queries?
The following variation on yours should work:
SELECT A.id, COUNT(distinct B.id), COUNT(distinct C.id)
FROM A LEFT JOIN
B
ON A.id = B.a_id LEFT JOIN
C
ON A.id = C.a_id
GROUP BY A.id
However, there are those (such as myself) who feel that using count distinct is a cop-out. The problem is that the rows from B and from C are interfering with each other, multiplying in the join. So, you can also do each join independently, and then put the results together:
select ab.id, cntB, cntC
from (select a.id, count(*) as cntB
from A left outer join
B
on A.id = B.a_id
group by a.id
) ab join
(select a.id, count(*) as cntC
from A left outer join
C
on A.id = C.a_id
group by a.id
) ac
on ab.id = ac.id
For just counting, the first form is fine. If you need to do other summarizations (say, summing a value), then you generally need to split into the component queries.