SQL Query to sum fields from different tables - sql

I'm a humble programmer that hates SQL ... :) Please help me with this query.
I have 4 tables, for example:
Table A:
Id Total
1 100
2 200
3 500
Table B
ExtId Amount
1 10
1 20
1 13
2 12
2 43
3 43
3 22
Table C
ExtId Amount
1 10
1 20
1 13
2 12
2 43
3 43
3 22
Table D
ExtId Amount
1 10
1 20
1 13
2 12
2 43
3 43
3 22
I need to make a SELECT that shows the Id, the Total and the SUM of the Amount fields of tables B, C and D like this
Id Total AmountB AmountC AmountD
1 100 43 43 43
2 200 55 55 55
3 500 65 65 65
I've tried with a inner join of the three tables by the Id and doing a sum of the amount fields but results are not rigth. Here is the wrong query:
SELECT dbo.A.Id, dbo.A.Total, SUM(dbo.B.Amount) AS Expr1, SUM(dbo.C.Amount) AS Expr2, SUM(dbo.D.Amount) AS Expr3
FROM dbo.A INNER JOIN
dbo.B ON dbo.A.Id = dbo.B.ExtId INNER JOIN
dbo.C ON dbo.A.Id = dbo.C.ExtId INNER JOIN
dbo.D ON dbo.A.Id = dbo.D.ExtId
GROUP BY dbo.A.Id, dbo.A.Total
Thanks in advance, its just that I hate SQL (or that SQL hates me).
EDIT: I had a typo. This query is not giving the right results. Extended the example.

Or you can take advantage of using SubQueries:
select A.ID, A.Total, b.SB as AmountB, c.SC as AmountC, d.SD as AmountD
from A
inner join (select ExtID, sum(Amount) as SB from B group by ExtID) b on A.ID = b.ExtID
inner join (select ExtID, sum(Amount) as SC from C group by ExtID) c on c.ExtID = A.ID
inner join (select ExtID, sum(Amount) as SD from D group by ExtID) d on d.ExtID = A.ID

From your description, this query should give you an error as you are using the non-existent column dbo.A.Amount in your group by. Changing this to dbo.A.Total might be what you need.
If you need all the amounts together, then try this query:
select A.Id, A.Total, sum(B.Amount + C.Amount + D.Amount) AS Total_Amount
from A
inner join B on A.Id = B.ExtId
inner join C on A.Id = C.ExtId
inner join D on A.Id = D.ExtId
group by A.Id, A.Total;

This one also works well
SELECT (SELECT SUM(Amount) FROM TableA) AS AmountA,
(SELECT SUM(Amount) FROM TableB) AS AmountB,
(SELECT SUM(Amount) FROM TableC) AS AmountC,
(SELECT SUM(Amount) FROM TableD) AS AmountD

This might help other users.
SELECT Total=(Select Sum(Amount) from table a)+(Select Sum(Amount) from table b)+(Select Sum(Amount) from table c)

Try this code
SELECT Total=isnull((Select Sum(Isnull(Amount,0)) from table a),0)+isnull((Select Sum(isnull(Amount,0)) from table b),0)+isnull((Select Sum(isnull(Amount,0)) from table c),0)

Related

How to generate loop kind of behaviour in SQL query to fetch multiple queries & compare results?

I am unable to generate a looping kind of behaviour in a SQL query.
I am having two tables:
Table A
Id Brand Prod_Id Alt_Prod_Id
1 A 2 5
2 B 3 9
3 C 5 9
Table B
Id Prod_Id Rate
1 2 5
2 3 9
2 5 7
2 9 9
Rate in Table B needs to be looked up for each brands Prod_ID & Alt_Prod_Id & select the least value between 2 found value
The expected result / output is:
Brand Min_Prod_Val
A 5
B 9
C 7
Can this be done in a query?
Thanks!
You could join tableb twice (once for prod_id, another for alt_prod_id), and then select the smallest rate:
select
a.brand,
least(b1.rate, b2.rate) min_prod_val
from tablea a
inner join tableb b1 on b1.prod_id = a.prod_id
inner join tableb b2 on b2.prod_id = a.alt_prod_id
It is unclear which database you are using. If that's SQL Server: it does not support least(), so you need a case expression:
case when b1.rate < b2.rate then b1.rate else b2.rate end min_prod_val
You can use a single join and GROUP BY the brand:
SELECT a.Brand,
MIN( b.rate ) AS min_prod_val
FROM TableA A
INNER JOIN TableB b
ON ( b.prod_id IN ( a.prod_id, a.alt_prod_id ) )
GROUP BY a.Brand
Or you can use a correlated sub-query:
SELECT a.Brand,
(
SELECT MIN( rate )
FROM TableB b
WHERE b.prod_id IN ( a.prod_id, a.alt_prod_id )
) AS min_prod_val
FROM TableA A
db<>fiddle

sql joining table, not providing perfect result

let me explain what i need, i have 2 table named A and B. B is sub table for A.
Here is Schema:
------------------------
Table B:
itemId version qty AId
44 1 1 200
44 1 2 201
44 2 2 200
------------------------
Table A:
id tId
200 100
201 100
------------------------
and here is what i need: i need sum of all latest version qty that have same tId.
here is my query:
select sum(qty) as sum from B
left join A on A.id=B.AId
where itemId=44 and tId=100 and
version=(select max(version) from B where itemId=44 and tId=100)
the result get wrong when one item got version 2 and version 1 ignored.
thanks.
EDIT:
what exactly i need is:
itemId version qty AId
44 2 2 200
44 1 2 201
And Result of Sum(qty) must be 4, because they have same tId and they have Max version in each AId.
Use window function.
select itemid, version, qty, aid
from (
select *, max(version) over (partition by AId) as latestVersion
from B
) as B
where version = latestVersion
to sum up
select tId, SUM(qty) AS qty_sum
from (
select *, max(version) over (partition by AId) as latestVersion
from B
) as B
join A on B.AId = A.id
where version = latestVersion
group by tId
Working solution
select b.* from B as b
inner join
(select AID,itemId,Max(version) as mVersion from B
group by AID,itemID) d
on b.AID = d.AID and b.itemID = d.itemID and b.Version = d.mVersion
inner join A as a
on B.AID = a.id
where b.itemID = 44 --apply if you need
result
itemid version qty aid
44 2 2 200
44 1 2 201
this will give you result as you sum of quantity
select itemID,sum(qty) from (
select b.* from B as b
inner join
(select AID,itemId,Max(version) as mVersion from B
group by AID,itemID) d
on b.AID = d.AID and b.itemID = d.itemID and b.Version = d.mVersion
inner join A as a
on B.AID = a.id
where b.itemID = 44 --apply if you need
) e group by itemID
result
itemid sum
44 4
Try This one
DECLARE #TA Table (id int,tid int)
DECLARE #TB Table (itemid int, version int,qty int,AID int)
INSERT INTO #TA
SELECT 200, 100
UNION ALL
SELECT 201, 100
INSERT INTO #TB
SELECT 44,1,1,201
UNION ALL
SELECT 44,1,2,200
UNION ALL
SELECT 44,2,3,200
UNION ALL
SELECT 44,2,5,201
DECLARE #tid int
SET #tid = 100
SELECT XB.* FROM #Tb XB INNER JOIN
(SELECT Version,Max(AID) Aid FROM #TA A INNER JOIN #TB B ON A.id = B.AID AND tid = #tid Group By Version) X
ON X.version = XB.version and XB.AID = X.Aid
i think this query help you to solve your problem
SELECT itemId, version, qty , AId FROM (
SELECT itemId, version, qty , AId FROM b
LEFT JOIN a ON (b.aid = a.id)
) temp
WHERE version = (SELECT MAX(version) FROM b WHERE b.aid = temp.aid)
and temp.tid = 100 and temp.itemId = 44
SELECT B.*
FROM B
INNER JOIN
(SELECT Aid,MAX(version) AS version FROM B WHERE itemId=44 GROUP BY AId) AS B1
ON B.Aid=B1.Aid
AND B.version=B1.version
INNER JOIN
(SELECT * FROM A WHERE tId=100) AS A
ON A.id=B.Aid
Order BY B.aid
For Sum of qty
SELECT SUM(B.qty)
FROM B
INNER JOIN
(SELECT Aid,MAX(version) AS version FROM B WHERE itemId=44 GROUP BY AId) AS B1
ON B.Aid=B1.Aid AND B.version=B1.version
INNER JOIN
(SELECT * FROM A WHERE tId=100) AS A
ON A.id=B.Aid
GROUP BY A.tid
Output
itemid version qty aid
44 2 2 200
44 1 2 201
Demo
http://sqlfiddle.com/#!17/092dd/5
The most efficient solution greatest-n-per-group problems in Postgres are typically using the (proprietary) operator distinct on ()
So to get the latest version for each a.id, you can use:
select distinct on (a.id) b.*
from a
join b on a.id = b.aid
order by a.id, b.version desc;
The above returns:
itemid | version | qty | aid
-------+---------+-----+----
44 | 2 | 2 | 200
44 | 1 | 2 | 201
You can then sum over the result:
select sum(qty)
from (
select distinct on (a.id) b.qty
from a
join b on a.id = b.aid
order by a.id, b.version desc
) t;
Note that normally an order by in a derived table is useless, but in this case it's needed because otherwise distinct on () wouldn't work.
Online example: http://rextester.com/DRHK19268

Joining multiple tables into one under one Id

I have dozen of tables with following format:
Table 1
[idA] [numA]
NULL 8
1 10
2 15
3 16
Table 2
[idB] [numB]
2 14
3 30
4 32
Table 3
[idC] [numC]
NULL 56
1 24
4 37
5 36
...
Now, I'm not sure how to formulate T-Sql query in order to produce following result:
[id] [numA] [numB] [numC] ...
NULL 8 0 56
1 10 0 24
2 15 14 0
3 16 30 0
4 0 32 37
5 0 0 36
Are there any suggestions on how to solve this?
I offer a solution with the full outer join, because that seems like the natural approach:
SELECT coalesce(a.id, b.id, c.id, . . .) as id,
a.NumA, b.NumB, c.NumC, . . .
FROM TableA a full outer join
TableB b
on a.id = b.id full outer join
TableC c
on coalesce(a.id, b.id) = c.id
However, the query needs to be written carefully, to keep the coalesces in line. The one advantage of this approach is that it should use indexes on the id columns for the query.
please try this
select id, max(numa),max(numb),max(numc) from
(
select id,numa,0 as numb,0 as numc from tb1
union all
select id,0 as numa,numb as numb,0 as numc from tb2
union all
select id,0 as numa,0 as numb,numc as numc from tb3
)X
group by id
order by id
Thanks
Rajath
SELECT Maintable.id,
Table1.numA,
Table2.numB,
Table3.numC
FROM (SELECT ida AS id
FROM Table1
UNION
SELECT idb AS id
FROM Table2
UNION
SELECT idc AS id
FROM Table3) MainTable
LEFT JOIN Table1
ON Maintable.id = Table1.Ida
LEFT JOIN Table2
ON Maintable.id = Table2.idB
LEFT JOIN Table3
ON Maintable.id = Table3.idC

Getting top row for each unique group in the inner joined group query

table 1
id name class
1 ab A
2 cd A
3 ef B
4 ab B
5 cd B
table 2
name test marks
ab 1 90
ab 2 70
cd 2 80
cd 3 85
ef 3 85
ef 4 60
Hi, I have 2 tables above, my question is what is the most efficient/best or simplest way to get the highest marks from table 2 for each person and join to table 1 such that returns:
id name class [highest marks]
1 ab A 90
2 cd A 85
3 ef B 85
Assuming SQL Server 2005+, using analytic/ranking/windowing functionality:
WITH example AS (
SELECT a.id,
a.name,
a.class,
b.marks,
ROW_NUMBER() OVER(PARTITION BY a.id
ORDER BY b.marks DESC) AS rank
FROM TABLE_1 a
JOIN TABLE_2 b ON b.name = a.name)
SELECT e.id,
e.name,
e.class,
e.marks
FROM example e
WHERE e.rank = 1
Using aggregates:
SELECT a.id,
a.name,
a.class,
b.marks
FROM TABLE_1 a
JOIN (SELECT t.name,
MAX(t.mark) AS max_mark
FROM TABLE_2
GROUP BY t.name) b ON b.name = a.name
Another option if you dont want to use CTE (Common Table Expressions)
SELECT table1.id, table1.name, table1.class, MAX(table2.marks) AS [highest marks]
FROM table1 INNER JOIN
table2 ON table1.name = table2.name
GROUP BY table1.id, table1.name, table1.class

How to get this result with and only with SQL?

The question is:
Two tables (t1, t2)
Table t1:
SELLER | NON_SELLER
A B
A C
A D
B A
B C
B D
C A
C B
C D
D A
D B
D C
Table t2:
SELLER | COUPON | BAL
A 9 100
B 9 200
C 9 300
D 9 400
A 9.5 100
B 9.5 20
A 10 80
Using SELECT Statement to get this result:
SELLER| COUPON | SUM(BAL)
A 9 900
B 9 800
C 9 700
D 9 600
A 9.5 20
B 9.5 100
C 9.5 120
D 9.5 120
A 10 0 # !!!
B 10 80
C 10 80
D 10 80
For seller A SUM(BAL) means sum( B.BAL,C.BAL,D.BAL), for B, SUM(BAL)=SUM(A.BAL,C.BAL,D.BAL)...
Please find a way with good performance and don't use temporary table.
My solution:
Running this query will get the result but without the row "A 10 0":
select t1.seller, t2.coupon, sum(bal)
from t1, t2
where t1.non_seller = t2.seller
group by t1.seller, t2.coupon
order by t2.coupon
Please help ~~~~~~
If I understand you correctly, you're looking for data on all sellers and all coupons. So let's start with a cross join that generates a list of coupons and sellers:
select sellers.seller
, coupons.coupon
from (
select distinct seller
from Table2
) as sellers
cross join
(
select distinct coupon
from Table2
) as coupons
For each seller-coupon combination, you're looking for the sum they can buy from other sellers. This can be accomplished by a left join:
select sellers.seller
, coupons.coupon
, case when sum(t2.bal) is null then 0 else sum(t2.bal) end
from (
select distinct seller
from Table2
) as sellers
cross join
(
select distinct coupon
from Table2
) as coupons
left join
Table2 t2
on t2.seller <> sellers.seller
and t2.coupon = coupons.coupon
group by
sellers.seller
, coupons.coupon
The only function of the case statement is to replace a null sum with a 0.
The output matches the one in your answer. Note that this solution doesn't use Table1: the list of other sellers is produced by the t2.seller <> sellers.seller condition in the left join.
I get another way to this:
select t1.seller, t2.coupon, sum(bal)
from t1, t2
where t1.non_seller = t2.seller
group by t1.seller, t2.coupon
union
(select seller,coupon,0 from t2 group by coupon having count(seller) == 1);
And I don't know if it is better or worst than compare with #Andomar :
select sellers.seller
, coupons.coupon
, case when sum(t2.bal) is null then 0 else sum(t2.bal) end
from (
select distinct seller
from Table2
) as sellers
cross join
(
select distinct coupon
from Table2
) as coupons
left join
Table2 t2
on t2.seller <> sellers.seller
and t2.coupon = coupons.coupon
group by
sellers.seller
, coupons.coupon