Join and sum multiple columns - sql-server

Table TB1:
| PaymentID | CashAmount |
--------------------------
| P1 | 3,000|
| P2 | 5,000|
| P3 | 8,000|
Table TB2:
|ChequeID| PaymentID |ChequeAmount|
-----------------------------------
| C1 | P2 | 10,000|
| C2 | P1 | 15,000|
| C3 | P1 | 2,000|
Table TB3:
|TransferID| PaymentID |TransferAmount|
---------------------------------------
| T1 | P2 | 20,000|
| T2 | P2 | 20,000|
| T3 | P1 | 3,000|
Expected Result
| PaymentID | CashAmount |ChequeAmount|TransferAmount|
------------------------------------------------------
| P1 | 3,000| 17,000| 3,000|
| P2 | 5,000| 10,000| 40,000|
| P3 | 8,000| NULL| NULL|
How to write a query JOIN and SUM all these tables together?
I tried writing simple join query but the result was wrong.
Example of incorrect query:
SELECT
TB1.PaymentID, TB1.CashAmount,
SUM(TB2.ChequeAmount) AS ChequeAmount,
SUM(TB3.TransferAmount) AS TransferAmount
FROM
TB1
LEFT JOIN
TB2 ON TB1.PaymentID = TB2.PaymentID
LEFT JOIN
TB3 ON TB1.PaymentID = TB3.PaymentID
GROUP BY
TB1.PaymentID, TB1.CashAmount
Incorrect result:
| PaymentID | CashAmount | ChequeAmount | TransferAmount |
----------------------------------------------------------
| P1 | 3,000 | 17,000 | 6,000 |
| P2 | 5,000 | 20,000 | 40,000 |
| P3 | 8,000 | NULL | NULL |

The problem you have is you're joining the tables that have multiple rows with the same id and it multiplies the numbers. You'll need to do the sum first and then join the data. Assuming the data can be missing from any of the tables, you'll also need to use full outer join. If TB1 has the row always, then left outer join is enough.
This is how you can do it:
SELECT
coalesce(TB1.PaymentID,TB2.PaymentID,TB3.PaymentID),
TB1.CashAmount, TB2.ChequeAmount, TB3.TransferAmount
from (
select PaymentID, SUM(CashAmount) AS CashAmount
from TB1 group by PaymentID
) TB1
full outer join (
select PaymentID, SUM(ChequeAmount) AS ChequeAmount
from TB2 group by PaymentID
) TB2 on TB1.PaymentID = TB2.PaymentID
full outer join (
select PaymentID, SUM(TransferAmount) AS TransferAmount
from TB3 group by PaymentID
) TB3 on isnull(TB1.PaymentID, TB2.PaymentID) = TB3.PaymentID
Example in SQL Fiddle

I would eliminate the duplicates in the minor tables first, then (left) join and sum with the major table, like this
SELECT
TB1.PaymentID,
TB1.CashAmount,
Sum(TB2.ChequeAmount) as ChequeAmount,
Sum(TB3.TransferAmount) as TranAmt
from TB1
left join (
select PaymentID, SUM(ChequeAmount) AS ChequeAmount
from TB2 group by PaymentID
) TB2 on TB1.PaymentID = TB2.PaymentID
left join (
select PaymentID, SUM(TB3.TransferAmount) AS TransferAmount
from TB3 group by PaymentID
) TB3 on isnull(TB1.PaymentID, TB2.PaymentID) = TB3.PaymentID
group by
tb1.PaymentId, tb1.CashAmount

WITH t2
AS (
SELECT [PaymentId], SUM([ChequeAmount]) AS ChequeAmount
FROM TB2
GROUP BY [PaymentId]
),
t3
AS (
SELECT [PaymentId], SUM([TransferAmount]) AS TransferAmount
FROM TB3
GROUP BY [PaymentId]
)
SELECT t.PaymentId, SUM(CashAmount) AS CashAmount,
SUM(t2.[ChequeAmount]) AS [ChequeAmount],
SUM(t3.[TransferAmount]) AS [TransferAmount]
FROM TB1 AS [t]
LEFT JOIN [t2] ON [t2].[PaymentId] = [t].[PaymentId]
LEFT JOIN [t3] ON [t3].[PaymentId] = [t].[PaymentId]
GROUP BY [t].[PaymentId];

Related

JOIN / MERGE several tables with same key

I have several tables that I have joined like this:
SELECT *
FROM tableA AS A
FULL OUTER JOIN tableB AS B
ON B.KEY = A.KEY
FULL OUTER JOIN tableC AS C
ON C.KEY = A.KEY
FULL OUTER JOIN tableD AS D
ON D.KEY = A.KEY
This works fine, but i sometime get separate rows where the key does not exist in all tables.
Example
WITH tableA([KEY], payloadColumn) AS
(
SELECT 1, 'var_A1'
), tableB([KEY], payloadColumn) AS
(
SELECT 2, 'var_B2'
), tableC([KEY], payloadColumn) AS
(
SELECT 1, 'var_C1'
), tableD([KEY], payloadColumn) AS
(
SELECT 2, 'var_D2'
)
SELECT * FROM tableA as A
FULL OUTER JOIN tableB as B ON B.[KEY] = A.[KEY]
FULL OUTER JOIN tableC as C ON C.[KEY] = A.[KEY]
FULL OUTER JOIN tableD as D ON D.[KEY] = A.[KEY]
Returns
+------+---------------+------+---------------+------+---------------+------+---------------+
| KEY | payloadColumn | KEY | payloadColumn | KEY | payloadColumn | KEY | payloadColumn |
+------+---------------+------+---------------+------+---------------+------+---------------+
| 1 | var_A1 | NULL | NULL | 1 | var_C1 | NULL | NULL |
| NULL | NULL | 2 | var_B2 | NULL | NULL | NULL | NULL |
| NULL | NULL | NULL | NULL | NULL | NULL | 2 | var_D2 |
+------+---------------+------+---------------+------+---------------+------+---------------+
But i need them all to be in the same row in the result (not two rows for 2). How can I do this?
That's my proposed solution based on the draft in a previous version of OP, and their last edit:
CREATE VIEW ABCD
AS
SELECT [KEY] FROM tableA
UNION
SELECT [KEY] FROM tableB
UNION
SELECT [KEY] FROM tableC
UNION
SELECT [KEY] FROM tableD
Creating a view may not be an option; in this case rewrite as a CTE.
At least in this way, the execution plan will continue to use indexes (assuming [KEY] is a PK or indexed).
SELECT
K.[KEY] as sameKey,
A.payloadColumn,
B.payloadColumn,
C.payloadColumn,
D.payloadColumn
FROM ABCD K
LEFT JOIN tableA A ON A.[KEY]=K.[KEY]
LEFT JOIN tableB B ON B.[KEY]=K.[KEY]
LEFT JOIN tableC C ON C.[KEY]=K.[KEY]
LEFT JOIN tableD D ON D.[KEY]=K.[KEY]
If this got your idea wrong, please elaborate.
You can use COALESCE
WITH tableA([KEY], payloadColumn)
AS (SELECT 1,
'var_A1'),
tableB([KEY], payloadColumn)
AS (SELECT 2,
'var_B2'),
tableC([KEY], payloadColumn)
AS (SELECT 1,
'var_C1'),
tableD([KEY], payloadColumn)
AS (SELECT 2,
'var_D2')
SELECT COALESCE(A.[KEY], B.[KEY], C.[KEY], D.[KEY]) AS [KEY],
A.payloadColumn,
B.payloadColumn,
C.payloadColumn,
D.payloadColumn
FROM tableA AS A
FULL OUTER JOIN tableB AS B
ON B.[KEY] = A.[KEY]
FULL OUTER JOIN tableC AS C
ON C.[KEY] = COALESCE(A.[KEY], B.[KEY])
FULL OUTER JOIN tableD AS D
ON D.[KEY] = COALESCE(A.[KEY], B.[KEY], C.[KEY]);
Returns
+-----+---------------+---------------+---------------+---------------+
| KEY | payloadColumn | payloadColumn | payloadColumn | payloadColumn |
+-----+---------------+---------------+---------------+---------------+
| 1 | var_A1 | NULL | var_C1 | NULL |
| 2 | NULL | var_B2 | NULL | var_D2 |
+-----+---------------+---------------+---------------+---------------+
If all the tables have a covering index on [KEY] INCLUDE (payloadColumn) the execution plan for this can just chain together merge joins.

SQL Server GROUP BY bigger than

I query and have a table with the following sample data
SELECT tb1.ID ,
tb2.TypeID ,
tb2.Name
FROM tb1
INNER JOIN tb2 ON tb1.ID = tb2.ID
WHERE tb1.Status = 1
GROUP BY tb1.ID ,
tb2.TypeID ,
tb2.Name
ID | TypeID | Name|
======================
11 | 1 | A |
19 | 1 | A |
10 | 2 | A |
5 | 1 | B |
4 | 2 | B |
20 | 1 | C |
21 | 1 | C |
I'd like to create a query that shows the ID for each distinct TypeID +Name more than 1, e.g.
ID |
=====
11 |
19 |
20 |
21 |
Can anyone give me a little help? Thanks.
Try like this,
SELECT tb1.ID
FROM tb1
INNER JOIN tb2 ON tb1.ID = tb2.ID
INNER JOIN (
SELECT tb2.TypeID
,tb2.NAME
FROM tb1
INNER JOIN tb2 ON tb1.ID = tb2.ID
WHERE tb1.STATUS = 1
GROUP BY tb2.TypeID
,tb2.NAME
HAVING COUNT(*) > 1
) tb3 ON tb3.typeid = tb2.typeid
AND tb3.NAME = tb2.NAME
WHERE tb1.STATUS = 1
You will need a subquery that will select all records grouped by those two fields and having 2 and more occurrence after that grouping.
SELECT tb1.ID FROM tb1 INNER JOIN tb2 ON tb1.ID = tb2.ID INNER JOIN (
SELECT tb2.TypeID ,tb2.name FROM tb1
INNER JOIN tb2 ON tb1.ID = tb2.ID WHERE tb1.status = 1
GROUP BY tb2.TypeID ,tb2.name HAVING COUNT(*) > 1
) temp ON temp.typeid = tb2.typeid
AND temp.name = tb2.Name
WHERE tb1.status = 1

Split table in two tables plus a link table

I have a table with three columns with double values, but no double rows. Now I want to split this table in two table with unique values and a link table. I think the Problem gets clearer when I Show you example tables:
Original:
| ID | Column_1 | Column_2 | Column_3 |
|----|----------|----------|----------|
| 1 | A | 123 | A1 |
| 2 | A | 123 | A2 |
| 3 | B | 234 | A2 |
| 4 | C | 456 | A1 |
Table_1
| ID | Column_1 | Column_2 |
|----|----------|----------|
| 1 | A | 123 |
| 2 | B | 234 |
| 3 | C | 456 |
Table_2
| ID | Column_3 |
|----|----------|
| 1 | A1 |
| 2 | A2 |
Link-Table
| ID | fk1 | fk2 |
|----|-----|-----|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
| 4 | 3 | 1 |
Table_1 I created like this:
INSERT INTO Table_1(Column_1, Column_2)
SELECT DISTINCT Column_1, Column_2 FROM Original
WHERE Original.Column_1 NOT IN (SELECT Column_1 FROM Table_1)
Table_2 I created in the same way.
The question now is, how to create the Link-Table?
The original table does grow continuesly, so only new entries should be added.
Do I have to use a Cursor, or is there a better way?
SOLUTION:
MERGE Link_Table AS LT
USING (SELECT DISTINCT T1.ID AS T1ID, T2.ID AS T2ID FROM Original AS O
INNER JOIN Table_1 AS T1 ON T1.Column_1 = O.Column_1
INNER JOIN Table_2 AS T2 ON T2.Column_3 = O.Column_3) AS U
ON LT.fk1 = U.T1ID
WHEN NOT MATCHED THEN
INSERT (fk1, fk2)
VALUES (U.T1ID, U.T2ID);
You can JOIN all 3 tables to get proper data for link table:
--INSERT INTO [Link-Table]
SELECT t1.ID,
t2.ID
FROM Original o
INNER JOIN Table_1 t1
ON t1.Column_1 = o.Column_1
INNER JOIN Table_2 t2
ON t2.Column_3 = o.Column_3
If your original table will grow, then you need to use MERGE to update/insert new data.
You have to inner join your Original,Table_1 and Table_2 to get the desired result.
Try like this, Its similar to gofr1 post.
DECLARE #orginal TABLE (
ID INT
,Column_1 VARCHAR(10)
,Column_2 INT
,Column_3 VARCHAR(10)
)
DECLARE #Table_1 TABLE (
ID INT
,Column_1 VARCHAR(10)
,Column_2 INT
)
DECLARE #Table_2 TABLE (
ID INT
,Column_3 VARCHAR(10)
)
Insert into #orginal values
(1,'A',123,'A1')
,(2,'A',123,'A2')
,(3,'B',234,'A2')
,(4,'C',456,'A1')
Insert into #Table_1 values
(1,'A',123)
,(2,'B',234)
,(3,'C',456)
Insert into #Table_2 values
(1,'A1')
,(2,'A2')
SELECT O.ID
,T1.ID
,T2.ID
FROM #orginal O
INNER JOIN #Table_1 T1 ON T1.Column_1 = O.Column_1
INNER JOIN #Table_2 T2 ON T2.Column_3 = O.Column_3

SELECT rows from Table1 having identical values on columns and Table2 Column > N

This is simple. I have two tables. I need to select rows from Table1 which have same 'Customer' and in Table2 'yearmm' is bigger than 2015001.
Table1
id | Customer | yearmmm |
----------------------------
10 | 123456 | 2015001 |
11 | 456789 | 2015001 |
20 | 111111 | 2015001 |
21 | 222222 | 2015001 |
44 | 4444 | 2015001 |
Table2
id | Customer | yearmmm |
----------------------------
10 | 123456 | 2015001 |
11 | 456789 | 2015002 |
20 | 111111 | 2015003 |
21 | 222222 | 2010001 |
333 | 333 | 2015004 |
Wonder if this works:
SELECT * FROM Table1 WHERE Customer IN
(SELECT Customer FROM Table2 WHERE yearmmm > '2015001')
Desired result:
11 | 456789 | 2015002 |
20 | 111111 | 2015003 |
You can use EXISTS:
SELECT t1.*
FROM Table1 t1
WHERE EXISTS
(
SELECT 1 FROM Table2 t2
WHERE t1.Customer = t2.Customer
AND t2.yearmmm > '20150101'
)
You have other options like INNER JOIN or IN.
Well, no, that will not work. You're ultimately selecting the Customer and yearmmm from Table1 based on values in Table2. Yet your desired results show yearmmm values that exist in Table2.
Based on your desired results it seems like you just want this:
SELECT * FROM Table2 WHERE yearmmm > '2015001'
EDIT: If you do in fact need more data from Table1, consider:
SELECT t1.id, t1.Customer, t2.yearmmm, another_other_fields
FROM Table1 t1 INNER JOIN Table2 t2 ON t1.id = t2.id
WHERE t2.yearmmm > '2015001'
You can use:
SELECT
t1.*
FROM
Table1 t1
INNER JOIN Table2 t2 ON
(t1.Customer = t2.Customer)
AND (t2.yearmmm > '2015001');
Or
SELECT
t1.*
FROM
Table1 t1
INNER JOIN Table2 t2 ON
(t1.Customer = t2.Customer)
WHERE
(t2.yearmmm > '2015001');

Select data from 3 tables in sql

I need to select data from 3 tables.
Please check the sample tables and expected output.
And also the fiddle link below.
Table1
Name | Image_Name
--------+--------------
A1 | A1.jpg
B1 | B1.jpg
C1 | C1.jpg
D1 | D1.jpg
E1 | E1.jpg
F1 | F1.jpg
G1 | G1.jpg
H1 | H1.jpg
I1 | I1.jpg
J1 | J1.jpg
Table2
Name | qty1
--------+----------
A1 | 1
B1 | 2
D1 | 3
F1 | 4
Table3
Name | qty2
--------+----------
A1 | 5
B1 | 6
J1 | 7
Expected Output
Name | Image_Name | qty1 | qty2
--------+---------------+---------+--------
A1 | A1.jpg | 1 | 5
B1 | B1.jpg | 2 | 6
D1 | D1.jpg | 3 | 0
F1 | F1.jpg | 4 | 0
J1 | J1.jpg | 0 | 7
Find the Fiddle here
The query:
SELECT a.Name,a.Image_Name,
b.qty1,c.qty2
FROM Table1 a
JOIN Table2 b
ON a.Name=b.Name
FULL JOIN Table3 c
ON b.Name=c.Name;
Try Following query:
SELECT table1.name, table1.Image_name, Table2.Qty1, Table3.Qty2
FROM table1
LEFT JOIN table2 on Table2.Name = table1.name
LEFT JOIN table3 on Table3.Name = table1.name
WHERE table2.name IS NOT NULL
OR table3.name IS NOT NULL
Two left joins would appear to do the trick:
select name
, image_name
, coalesce(t2.qty1, 0)
, coalesce(t3.qty2, 0)
from t1
left join
t2
on t1.Name = t2.Name
left join
t3
on t1.Name = t3.Name
Unlike the full join from your SQL Fiddle, a left join returns only rows from the right table if they are matched in the left table.
It looks like you just need to select the qty in the output list. This will return NULL for qty2 and qty3 if the value is not in table2 or table3 respectively.
select name, image_name,
(select qty1 from table2 where name = table1.name) as qty1,
(select qty2 from table3 where name = table1.name) as qty2
from table1
You can try the below;
Select tbl1.Name, tbl1.Image_Name, tbl2.qty1, tbl3.qty2
from Table1 tbl1
inner join Table2 tbl2
on tbl1.Name = tbl2.Name
inner join Table3 tbl3
on tbl1.Name = tbl3.Name

Resources